about summary refs log tree commit diff
path: root/app/javascript/themes/glitch/features
diff options
context:
space:
mode:
authorbeatrix <beatrix.bitrot@gmail.com>2017-12-06 17:44:07 -0500
committerGitHub <noreply@github.com>2017-12-06 17:44:07 -0500
commit81b01457598459c42a7b14d9aa14f91ba60dcae1 (patch)
tree7d3e6dadb75f3be95e5a5ed8b7ecfe90e7711831 /app/javascript/themes/glitch/features
parentf1cbea77a4a52929244198dcbde26d63d837489a (diff)
parent017fc81caf8f265e5c5543186877437485625795 (diff)
Merge pull request #229 from glitch-soc/glitch-theme
Advanced Next-Level Flavours And Skins For Mastodon™
Diffstat (limited to 'app/javascript/themes/glitch/features')
-rw-r--r--app/javascript/themes/glitch/features/account/components/action_bar.js145
-rw-r--r--app/javascript/themes/glitch/features/account/components/header.js99
-rw-r--r--app/javascript/themes/glitch/features/account_gallery/components/media_item.js39
-rw-r--r--app/javascript/themes/glitch/features/account_gallery/index.js111
-rw-r--r--app/javascript/themes/glitch/features/account_timeline/components/header.js95
-rw-r--r--app/javascript/themes/glitch/features/account_timeline/containers/header_container.js104
-rw-r--r--app/javascript/themes/glitch/features/account_timeline/index.js77
-rw-r--r--app/javascript/themes/glitch/features/blocks/index.js70
-rw-r--r--app/javascript/themes/glitch/features/community_timeline/components/column_settings.js35
-rw-r--r--app/javascript/themes/glitch/features/community_timeline/containers/column_settings_container.js17
-rw-r--r--app/javascript/themes/glitch/features/community_timeline/index.js107
-rw-r--r--app/javascript/themes/glitch/features/compose/components/advanced_options.js62
-rw-r--r--app/javascript/themes/glitch/features/compose/components/advanced_options_toggle.js35
-rw-r--r--app/javascript/themes/glitch/features/compose/components/attach_options.js131
-rw-r--r--app/javascript/themes/glitch/features/compose/components/autosuggest_account.js24
-rw-r--r--app/javascript/themes/glitch/features/compose/components/character_counter.js25
-rw-r--r--app/javascript/themes/glitch/features/compose/components/compose_form.js286
-rw-r--r--app/javascript/themes/glitch/features/compose/components/dropdown.js77
-rw-r--r--app/javascript/themes/glitch/features/compose/components/emoji_picker_dropdown.js376
-rw-r--r--app/javascript/themes/glitch/features/compose/components/navigation_bar.js38
-rw-r--r--app/javascript/themes/glitch/features/compose/components/privacy_dropdown.js200
-rw-r--r--app/javascript/themes/glitch/features/compose/components/reply_indicator.js63
-rw-r--r--app/javascript/themes/glitch/features/compose/components/search.js129
-rw-r--r--app/javascript/themes/glitch/features/compose/components/search_results.js65
-rw-r--r--app/javascript/themes/glitch/features/compose/components/text_icon_button.js29
-rw-r--r--app/javascript/themes/glitch/features/compose/components/upload.js96
-rw-r--r--app/javascript/themes/glitch/features/compose/components/upload_button.js77
-rw-r--r--app/javascript/themes/glitch/features/compose/components/upload_form.js29
-rw-r--r--app/javascript/themes/glitch/features/compose/components/upload_progress.js42
-rw-r--r--app/javascript/themes/glitch/features/compose/components/warning.js26
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/advanced_options_container.js20
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/autosuggest_account_container.js15
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/compose_form_container.js71
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/emoji_picker_dropdown_container.js82
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/navigation_container.js11
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/privacy_dropdown_container.js24
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/reply_indicator_container.js24
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/search_container.js35
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/search_results_container.js8
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/sensitive_button_container.js71
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/spoiler_button_container.js25
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/upload_button_container.js18
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/upload_container.js21
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/upload_form_container.js8
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/upload_progress_container.js9
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/warning_container.js24
-rw-r--r--app/javascript/themes/glitch/features/compose/index.js126
-rw-r--r--app/javascript/themes/glitch/features/direct_timeline/containers/column_settings_container.js17
-rw-r--r--app/javascript/themes/glitch/features/direct_timeline/index.js107
-rw-r--r--app/javascript/themes/glitch/features/favourited_statuses/index.js94
-rw-r--r--app/javascript/themes/glitch/features/favourites/index.js60
-rw-r--r--app/javascript/themes/glitch/features/follow_requests/components/account_authorize.js49
-rw-r--r--app/javascript/themes/glitch/features/follow_requests/containers/account_authorize_container.js26
-rw-r--r--app/javascript/themes/glitch/features/follow_requests/index.js71
-rw-r--r--app/javascript/themes/glitch/features/followers/index.js93
-rw-r--r--app/javascript/themes/glitch/features/following/index.js93
-rw-r--r--app/javascript/themes/glitch/features/generic_not_found/index.js11
-rw-r--r--app/javascript/themes/glitch/features/getting_started/index.js145
-rw-r--r--app/javascript/themes/glitch/features/hashtag_timeline/index.js118
-rw-r--r--app/javascript/themes/glitch/features/home_timeline/components/column_settings.js46
-rw-r--r--app/javascript/themes/glitch/features/home_timeline/containers/column_settings_container.js21
-rw-r--r--app/javascript/themes/glitch/features/home_timeline/index.js90
-rw-r--r--app/javascript/themes/glitch/features/local_settings/index.js68
-rw-r--r--app/javascript/themes/glitch/features/local_settings/navigation/index.js74
-rw-r--r--app/javascript/themes/glitch/features/local_settings/navigation/item/index.js69
-rw-r--r--app/javascript/themes/glitch/features/local_settings/navigation/item/style.scss27
-rw-r--r--app/javascript/themes/glitch/features/local_settings/navigation/style.scss10
-rw-r--r--app/javascript/themes/glitch/features/local_settings/page/index.js212
-rw-r--r--app/javascript/themes/glitch/features/local_settings/page/item/index.js90
-rw-r--r--app/javascript/themes/glitch/features/local_settings/page/item/style.scss7
-rw-r--r--app/javascript/themes/glitch/features/local_settings/page/style.scss9
-rw-r--r--app/javascript/themes/glitch/features/local_settings/style.scss34
-rw-r--r--app/javascript/themes/glitch/features/mutes/index.js70
-rw-r--r--app/javascript/themes/glitch/features/notifications/components/clear_column_button.js17
-rw-r--r--app/javascript/themes/glitch/features/notifications/components/column_settings.js86
-rw-r--r--app/javascript/themes/glitch/features/notifications/components/follow.js98
-rw-r--r--app/javascript/themes/glitch/features/notifications/components/notification.js93
-rw-r--r--app/javascript/themes/glitch/features/notifications/components/overlay.js57
-rw-r--r--app/javascript/themes/glitch/features/notifications/components/setting_toggle.js34
-rw-r--r--app/javascript/themes/glitch/features/notifications/containers/column_settings_container.js44
-rw-r--r--app/javascript/themes/glitch/features/notifications/containers/notification_container.js26
-rw-r--r--app/javascript/themes/glitch/features/notifications/containers/overlay_container.js18
-rw-r--r--app/javascript/themes/glitch/features/notifications/index.js193
-rw-r--r--app/javascript/themes/glitch/features/pinned_statuses/index.js59
-rw-r--r--app/javascript/themes/glitch/features/public_timeline/containers/column_settings_container.js17
-rw-r--r--app/javascript/themes/glitch/features/public_timeline/index.js107
-rw-r--r--app/javascript/themes/glitch/features/reblogs/index.js60
-rw-r--r--app/javascript/themes/glitch/features/report/components/status_check_box.js37
-rw-r--r--app/javascript/themes/glitch/features/report/containers/status_check_box_container.js19
-rw-r--r--app/javascript/themes/glitch/features/standalone/compose/index.js20
-rw-r--r--app/javascript/themes/glitch/features/standalone/hashtag_timeline/index.js70
-rw-r--r--app/javascript/themes/glitch/features/standalone/public_timeline/index.js76
-rw-r--r--app/javascript/themes/glitch/features/status/components/action_bar.js129
-rw-r--r--app/javascript/themes/glitch/features/status/components/card.js125
-rw-r--r--app/javascript/themes/glitch/features/status/components/detailed_status.js130
-rw-r--r--app/javascript/themes/glitch/features/status/containers/card_container.js8
-rw-r--r--app/javascript/themes/glitch/features/status/index.js358
-rw-r--r--app/javascript/themes/glitch/features/ui/components/actions_modal.js74
-rw-r--r--app/javascript/themes/glitch/features/ui/components/boost_modal.js84
-rw-r--r--app/javascript/themes/glitch/features/ui/components/bundle.js102
-rw-r--r--app/javascript/themes/glitch/features/ui/components/bundle_column_error.js44
-rw-r--r--app/javascript/themes/glitch/features/ui/components/bundle_modal_error.js53
-rw-r--r--app/javascript/themes/glitch/features/ui/components/column.js74
-rw-r--r--app/javascript/themes/glitch/features/ui/components/column_header.js35
-rw-r--r--app/javascript/themes/glitch/features/ui/components/column_link.js39
-rw-r--r--app/javascript/themes/glitch/features/ui/components/column_loading.js30
-rw-r--r--app/javascript/themes/glitch/features/ui/components/column_subheading.js16
-rw-r--r--app/javascript/themes/glitch/features/ui/components/columns_area.js174
-rw-r--r--app/javascript/themes/glitch/features/ui/components/confirmation_modal.js53
-rw-r--r--app/javascript/themes/glitch/features/ui/components/doodle_modal.js614
-rw-r--r--app/javascript/themes/glitch/features/ui/components/drawer_loading.js11
-rw-r--r--app/javascript/themes/glitch/features/ui/components/embed_modal.js84
-rw-r--r--app/javascript/themes/glitch/features/ui/components/image_loader.js152
-rw-r--r--app/javascript/themes/glitch/features/ui/components/media_modal.js126
-rw-r--r--app/javascript/themes/glitch/features/ui/components/modal_loading.js20
-rw-r--r--app/javascript/themes/glitch/features/ui/components/modal_root.js131
-rw-r--r--app/javascript/themes/glitch/features/ui/components/mute_modal.js105
-rw-r--r--app/javascript/themes/glitch/features/ui/components/onboarding_modal.js323
-rw-r--r--app/javascript/themes/glitch/features/ui/components/report_modal.js105
-rw-r--r--app/javascript/themes/glitch/features/ui/components/tabs_bar.js84
-rw-r--r--app/javascript/themes/glitch/features/ui/components/upload_area.js52
-rw-r--r--app/javascript/themes/glitch/features/ui/components/video_modal.js33
-rw-r--r--app/javascript/themes/glitch/features/ui/containers/bundle_container.js19
-rw-r--r--app/javascript/themes/glitch/features/ui/containers/columns_area_container.js8
-rw-r--r--app/javascript/themes/glitch/features/ui/containers/loading_bar_container.js8
-rw-r--r--app/javascript/themes/glitch/features/ui/containers/modal_container.js16
-rw-r--r--app/javascript/themes/glitch/features/ui/containers/notifications_container.js18
-rw-r--r--app/javascript/themes/glitch/features/ui/containers/status_list_container.js73
-rw-r--r--app/javascript/themes/glitch/features/ui/index.js443
-rw-r--r--app/javascript/themes/glitch/features/video/index.js288
130 files changed, 0 insertions, 10384 deletions
diff --git a/app/javascript/themes/glitch/features/account/components/action_bar.js b/app/javascript/themes/glitch/features/account/components/action_bar.js
deleted file mode 100644
index 0edd5c848..000000000
--- a/app/javascript/themes/glitch/features/account/components/action_bar.js
+++ /dev/null
@@ -1,145 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import DropdownMenuContainer from 'themes/glitch/containers/dropdown_menu_container';
-import { Link } from 'react-router-dom';
-import { defineMessages, injectIntl, FormattedMessage, FormattedNumber } from 'react-intl';
-import { me } from 'themes/glitch/util/initial_state';
-
-const messages = defineMessages({
-  mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
-  edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
-  unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
-  unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
-  unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
-  block: { id: 'account.block', defaultMessage: 'Block @{name}' },
-  mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
-  follow: { id: 'account.follow', defaultMessage: 'Follow' },
-  report: { id: 'account.report', defaultMessage: 'Report @{name}' },
-  share: { id: 'account.share', defaultMessage: 'Share @{name}\'s profile' },
-  media: { id: 'account.media', defaultMessage: 'Media' },
-  blockDomain: { id: 'account.block_domain', defaultMessage: 'Hide everything from {domain}' },
-  unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
-  hideReblogs: { id: 'account.hide_reblogs', defaultMessage: 'Hide boosts from @{name}' },
-  showReblogs: { id: 'account.show_reblogs', defaultMessage: 'Show boosts from @{name}' },
-});
-
-@injectIntl
-export default class ActionBar extends React.PureComponent {
-
-  static propTypes = {
-    account: ImmutablePropTypes.map.isRequired,
-    onFollow: PropTypes.func,
-    onBlock: PropTypes.func.isRequired,
-    onMention: PropTypes.func.isRequired,
-    onReblogToggle: PropTypes.func.isRequired,
-    onReport: PropTypes.func.isRequired,
-    onMute: PropTypes.func.isRequired,
-    onBlockDomain: PropTypes.func.isRequired,
-    onUnblockDomain: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  handleShare = () => {
-    navigator.share({
-      url: this.props.account.get('url'),
-    });
-  }
-
-  render () {
-    const { account, intl } = this.props;
-
-    let menu = [];
-    let extraInfo = '';
-
-    menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.props.onMention });
-    if ('share' in navigator) {
-      menu.push({ text: intl.formatMessage(messages.share, { name: account.get('username') }), action: this.handleShare });
-    }
-    menu.push(null);
-    menu.push({ text: intl.formatMessage(messages.media), to: `/accounts/${account.get('id')}/media` });
-    menu.push(null);
-
-    if (account.get('id') === me) {
-      menu.push({ text: intl.formatMessage(messages.edit_profile), href: '/settings/profile' });
-    } else {
-      const following = account.getIn(['relationship', 'following']);
-      if (following) {
-        if (following.get('reblogs')) {
-          menu.push({ text: intl.formatMessage(messages.hideReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
-        } else {
-          menu.push({ text: intl.formatMessage(messages.showReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
-        }
-      }
-
-      if (account.getIn(['relationship', 'muting'])) {
-        menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.props.onMute });
-      } else {
-        menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.props.onMute });
-      }
-
-      if (account.getIn(['relationship', 'blocking'])) {
-        menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.props.onBlock });
-      } else {
-        menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.props.onBlock });
-      }
-
-      menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.props.onReport });
-    }
-
-    if (account.get('acct') !== account.get('username')) {
-      const domain = account.get('acct').split('@')[1];
-
-      extraInfo = (
-        <div className='account__disclaimer'>
-          <FormattedMessage
-            id='account.disclaimer_full'
-            defaultMessage="Information below may reflect the user's profile incompletely."
-          />
-          {' '}
-          <a target='_blank' rel='noopener' href={account.get('url')}>
-            <FormattedMessage id='account.view_full_profile' defaultMessage='View full profile' />
-          </a>
-        </div>
-      );
-
-      menu.push(null);
-
-      if (account.getIn(['relationship', 'domain_blocking'])) {
-        menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain }), action: this.props.onUnblockDomain });
-      } else {
-        menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.props.onBlockDomain });
-      }
-    }
-
-    return (
-      <div>
-        {extraInfo}
-
-        <div className='account__action-bar'>
-          <div className='account__action-bar-dropdown'>
-            <DropdownMenuContainer items={menu} icon='bars' size={24} direction='right' />
-          </div>
-
-          <div className='account__action-bar-links'>
-            <Link className='account__action-bar__tab' to={`/accounts/${account.get('id')}`}>
-              <span><FormattedMessage id='account.posts' defaultMessage='Posts' /></span>
-              <strong><FormattedNumber value={account.get('statuses_count')} /></strong>
-            </Link>
-
-            <Link className='account__action-bar__tab' to={`/accounts/${account.get('id')}/following`}>
-              <span><FormattedMessage id='account.follows' defaultMessage='Follows' /></span>
-              <strong><FormattedNumber value={account.get('following_count')} /></strong>
-            </Link>
-
-            <Link className='account__action-bar__tab' to={`/accounts/${account.get('id')}/followers`}>
-              <span><FormattedMessage id='account.followers' defaultMessage='Followers' /></span>
-              <strong><FormattedNumber value={account.get('followers_count')} /></strong>
-            </Link>
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/account/components/header.js b/app/javascript/themes/glitch/features/account/components/header.js
deleted file mode 100644
index 696bb1991..000000000
--- a/app/javascript/themes/glitch/features/account/components/header.js
+++ /dev/null
@@ -1,99 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-import Avatar from 'themes/glitch/components/avatar';
-import IconButton from 'themes/glitch/components/icon_button';
-
-import emojify from 'themes/glitch/util/emoji';
-import { me } from 'themes/glitch/util/initial_state';
-import { processBio } from 'themes/glitch/util/bio_metadata';
-
-const messages = defineMessages({
-  unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
-  follow: { id: 'account.follow', defaultMessage: 'Follow' },
-  requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' },
-});
-
-@injectIntl
-export default class Header extends ImmutablePureComponent {
-
-  static propTypes = {
-    account: ImmutablePropTypes.map,
-    onFollow: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  render () {
-    const { account, intl } = this.props;
-
-    if (!account) {
-      return null;
-    }
-
-    let displayName = account.get('display_name_html');
-    let info        = '';
-    let actionBtn   = '';
-
-    if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) {
-      info = <span className='account--follows-info'><FormattedMessage id='account.follows_you' defaultMessage='Follows you' /></span>;
-    }
-
-    if (me !== account.get('id')) {
-      if (account.getIn(['relationship', 'requested'])) {
-        actionBtn = (
-          <div className='account--action-button'>
-            <IconButton size={26} active icon='hourglass' title={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />
-          </div>
-        );
-      } else if (!account.getIn(['relationship', 'blocking'])) {
-        actionBtn = (
-          <div className='account--action-button'>
-            <IconButton size={26} icon={account.getIn(['relationship', 'following']) ? 'user-times' : 'user-plus'} active={account.getIn(['relationship', 'following'])} title={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.props.onFollow} />
-          </div>
-        );
-      }
-    }
-
-    const { text, metadata } = processBio(account.get('note'));
-
-    return (
-      <div className='account__header__wrapper'>
-        <div className='account__header' style={{ backgroundImage: `url(${account.get('header')})` }}>
-          <div>
-            <Avatar account={account} size={90} />
-
-            <span className='account__header__display-name' dangerouslySetInnerHTML={{ __html: displayName }} />
-            <span className='account__header__username'>@{account.get('acct')} {account.get('locked') ? <i className='fa fa-lock' /> : null}</span>
-            <div className='account__header__content' dangerouslySetInnerHTML={{ __html: emojify(text) }} />
-
-            {info}
-            {actionBtn}
-          </div>
-        </div>
-
-        {metadata.length && (
-          <table className='account__metadata'>
-            <tbody>
-              {(() => {
-                let data = [];
-                for (let i = 0; i < metadata.length; i++) {
-                  data.push(
-                    <tr key={i}>
-                      <th scope='row'><div dangerouslySetInnerHTML={{ __html: emojify(metadata[i][0]) }} /></th>
-                      <td><div dangerouslySetInnerHTML={{ __html: emojify(metadata[i][1]) }} /></td>
-                    </tr>
-                  );
-                }
-                return data;
-              })()}
-            </tbody>
-          </table>
-        ) || null}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/account_gallery/components/media_item.js b/app/javascript/themes/glitch/features/account_gallery/components/media_item.js
deleted file mode 100644
index 88c9156b5..000000000
--- a/app/javascript/themes/glitch/features/account_gallery/components/media_item.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import Permalink from 'themes/glitch/components/permalink';
-
-export default class MediaItem extends ImmutablePureComponent {
-
-  static propTypes = {
-    media: ImmutablePropTypes.map.isRequired,
-  };
-
-  render () {
-    const { media } = this.props;
-    const status = media.get('status');
-
-    let content, style;
-
-    if (media.get('type') === 'gifv') {
-      content = <span className='media-gallery__gifv__label'>GIF</span>;
-    }
-
-    if (!status.get('sensitive')) {
-      style = { backgroundImage: `url(${media.get('preview_url')})` };
-    }
-
-    return (
-      <div className='account-gallery__item'>
-        <Permalink
-          to={`/statuses/${status.get('id')}`}
-          href={status.get('url')}
-          style={style}
-        >
-          {content}
-        </Permalink>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/account_gallery/index.js b/app/javascript/themes/glitch/features/account_gallery/index.js
deleted file mode 100644
index a21c089da..000000000
--- a/app/javascript/themes/glitch/features/account_gallery/index.js
+++ /dev/null
@@ -1,111 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import { fetchAccount } from 'themes/glitch/actions/accounts';
-import { refreshAccountMediaTimeline, expandAccountMediaTimeline } from 'themes/glitch/actions/timelines';
-import LoadingIndicator from 'themes/glitch/components/loading_indicator';
-import Column from 'themes/glitch/features/ui/components/column';
-import ColumnBackButton from 'themes/glitch/components/column_back_button';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { getAccountGallery } from 'themes/glitch/selectors';
-import MediaItem from './components/media_item';
-import HeaderContainer from 'themes/glitch/features/account_timeline/containers/header_container';
-import { FormattedMessage } from 'react-intl';
-import { ScrollContainer } from 'react-router-scroll-4';
-import LoadMore from 'themes/glitch/components/load_more';
-
-const mapStateToProps = (state, props) => ({
-  medias: getAccountGallery(state, props.params.accountId),
-  isLoading: state.getIn(['timelines', `account:${props.params.accountId}:media`, 'isLoading']),
-  hasMore: !!state.getIn(['timelines', `account:${props.params.accountId}:media`, 'next']),
-});
-
-@connect(mapStateToProps)
-export default class AccountGallery extends ImmutablePureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    medias: ImmutablePropTypes.list.isRequired,
-    isLoading: PropTypes.bool,
-    hasMore: PropTypes.bool,
-  };
-
-  componentDidMount () {
-    this.props.dispatch(fetchAccount(this.props.params.accountId));
-    this.props.dispatch(refreshAccountMediaTimeline(this.props.params.accountId));
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) {
-      this.props.dispatch(fetchAccount(nextProps.params.accountId));
-      this.props.dispatch(refreshAccountMediaTimeline(this.props.params.accountId));
-    }
-  }
-
-  handleScrollToBottom = () => {
-    if (this.props.hasMore) {
-      this.props.dispatch(expandAccountMediaTimeline(this.props.params.accountId));
-    }
-  }
-
-  handleScroll = (e) => {
-    const { scrollTop, scrollHeight, clientHeight } = e.target;
-    const offset = scrollHeight - scrollTop - clientHeight;
-
-    if (150 > offset && !this.props.isLoading) {
-      this.handleScrollToBottom();
-    }
-  }
-
-  handleLoadMore = (e) => {
-    e.preventDefault();
-    this.handleScrollToBottom();
-  }
-
-  render () {
-    const { medias, isLoading, hasMore } = this.props;
-
-    let loadMore = null;
-
-    if (!medias && isLoading) {
-      return (
-        <Column>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
-    if (!isLoading && medias.size > 0 && hasMore) {
-      loadMore = <LoadMore onClick={this.handleLoadMore} />;
-    }
-
-    return (
-      <Column>
-        <ColumnBackButton />
-
-        <ScrollContainer scrollKey='account_gallery'>
-          <div className='scrollable' onScroll={this.handleScroll}>
-            <HeaderContainer accountId={this.props.params.accountId} />
-
-            <div className='account-section-headline'>
-              <FormattedMessage id='account.media' defaultMessage='Media' />
-            </div>
-
-            <div className='account-gallery__container'>
-              {medias.map(media =>
-                <MediaItem
-                  key={media.get('id')}
-                  media={media}
-                />
-              )}
-              {loadMore}
-            </div>
-          </div>
-        </ScrollContainer>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/account_timeline/components/header.js b/app/javascript/themes/glitch/features/account_timeline/components/header.js
deleted file mode 100644
index c719a7bcb..000000000
--- a/app/javascript/themes/glitch/features/account_timeline/components/header.js
+++ /dev/null
@@ -1,95 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import InnerHeader from 'themes/glitch/features/account/components/header';
-import ActionBar from 'themes/glitch/features/account/components/action_bar';
-import MissingIndicator from 'themes/glitch/components/missing_indicator';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-export default class Header extends ImmutablePureComponent {
-
-  static propTypes = {
-    account: ImmutablePropTypes.map,
-    onFollow: PropTypes.func.isRequired,
-    onBlock: PropTypes.func.isRequired,
-    onMention: PropTypes.func.isRequired,
-    onReblogToggle: PropTypes.func.isRequired,
-    onReport: PropTypes.func.isRequired,
-    onMute: PropTypes.func.isRequired,
-    onBlockDomain: PropTypes.func.isRequired,
-    onUnblockDomain: PropTypes.func.isRequired,
-  };
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  handleFollow = () => {
-    this.props.onFollow(this.props.account);
-  }
-
-  handleBlock = () => {
-    this.props.onBlock(this.props.account);
-  }
-
-  handleMention = () => {
-    this.props.onMention(this.props.account, this.context.router.history);
-  }
-
-  handleReport = () => {
-    this.props.onReport(this.props.account);
-  }
-
-  handleReblogToggle = () => {
-    this.props.onReblogToggle(this.props.account);
-  }
-
-  handleMute = () => {
-    this.props.onMute(this.props.account);
-  }
-
-  handleBlockDomain = () => {
-    const domain = this.props.account.get('acct').split('@')[1];
-
-    if (!domain) return;
-
-    this.props.onBlockDomain(domain, this.props.account.get('id'));
-  }
-
-  handleUnblockDomain = () => {
-    const domain = this.props.account.get('acct').split('@')[1];
-
-    if (!domain) return;
-
-    this.props.onUnblockDomain(domain, this.props.account.get('id'));
-  }
-
-  render () {
-    const { account } = this.props;
-
-    if (account === null) {
-      return <MissingIndicator />;
-    }
-
-    return (
-      <div className='account-timeline__header'>
-        <InnerHeader
-          account={account}
-          onFollow={this.handleFollow}
-        />
-
-        <ActionBar
-          account={account}
-          onBlock={this.handleBlock}
-          onMention={this.handleMention}
-          onReblogToggle={this.handleReblogToggle}
-          onReport={this.handleReport}
-          onMute={this.handleMute}
-          onBlockDomain={this.handleBlockDomain}
-          onUnblockDomain={this.handleUnblockDomain}
-        />
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/account_timeline/containers/header_container.js b/app/javascript/themes/glitch/features/account_timeline/containers/header_container.js
deleted file mode 100644
index 766b57b56..000000000
--- a/app/javascript/themes/glitch/features/account_timeline/containers/header_container.js
+++ /dev/null
@@ -1,104 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import { makeGetAccount } from 'themes/glitch/selectors';
-import Header from '../components/header';
-import {
-  followAccount,
-  unfollowAccount,
-  blockAccount,
-  unblockAccount,
-  unmuteAccount,
-} from 'themes/glitch/actions/accounts';
-import { mentionCompose } from 'themes/glitch/actions/compose';
-import { initMuteModal } from 'themes/glitch/actions/mutes';
-import { initReport } from 'themes/glitch/actions/reports';
-import { openModal } from 'themes/glitch/actions/modal';
-import { blockDomain, unblockDomain } from 'themes/glitch/actions/domain_blocks';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import { unfollowModal } from 'themes/glitch/util/initial_state';
-
-const messages = defineMessages({
-  unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
-  blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' },
-  blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
-});
-
-const makeMapStateToProps = () => {
-  const getAccount = makeGetAccount();
-
-  const mapStateToProps = (state, { accountId }) => ({
-    account: getAccount(state, accountId),
-  });
-
-  return mapStateToProps;
-};
-
-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')));
-    }
-  },
-
-  onBlock (account) {
-    if (account.getIn(['relationship', 'blocking'])) {
-      dispatch(unblockAccount(account.get('id')));
-    } else {
-      dispatch(openModal('CONFIRM', {
-        message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
-        confirm: intl.formatMessage(messages.blockConfirm),
-        onConfirm: () => dispatch(blockAccount(account.get('id'))),
-      }));
-    }
-  },
-
-  onMention (account, router) {
-    dispatch(mentionCompose(account, router));
-  },
-
-  onReblogToggle (account) {
-    if (account.getIn(['relationship', 'following', 'reblogs'])) {
-      dispatch(followAccount(account.get('id'), false));
-    } else {
-      dispatch(followAccount(account.get('id'), true));
-    }
-  },
-
-  onReport (account) {
-    dispatch(initReport(account));
-  },
-
-  onMute (account) {
-    if (account.getIn(['relationship', 'muting'])) {
-      dispatch(unmuteAccount(account.get('id')));
-    } else {
-      dispatch(initMuteModal(account));
-    }
-  },
-
-  onBlockDomain (domain, accountId) {
-    dispatch(openModal('CONFIRM', {
-      message: <FormattedMessage id='confirmations.domain_block.message' defaultMessage='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.' values={{ domain: <strong>{domain}</strong> }} />,
-      confirm: intl.formatMessage(messages.blockDomainConfirm),
-      onConfirm: () => dispatch(blockDomain(domain, accountId)),
-    }));
-  },
-
-  onUnblockDomain (domain, accountId) {
-    dispatch(unblockDomain(domain, accountId));
-  },
-
-});
-
-export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Header));
diff --git a/app/javascript/themes/glitch/features/account_timeline/index.js b/app/javascript/themes/glitch/features/account_timeline/index.js
deleted file mode 100644
index 81336ef3a..000000000
--- a/app/javascript/themes/glitch/features/account_timeline/index.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import { fetchAccount } from 'themes/glitch/actions/accounts';
-import { refreshAccountTimeline, expandAccountTimeline } from 'themes/glitch/actions/timelines';
-import StatusList from '../../components/status_list';
-import LoadingIndicator from '../../components/loading_indicator';
-import Column from '../ui/components/column';
-import HeaderContainer from './containers/header_container';
-import ColumnBackButton from '../../components/column_back_button';
-import { List as ImmutableList } from 'immutable';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const mapStateToProps = (state, props) => ({
-  statusIds: state.getIn(['timelines', `account:${props.params.accountId}`, 'items'], ImmutableList()),
-  isLoading: state.getIn(['timelines', `account:${props.params.accountId}`, 'isLoading']),
-  hasMore: !!state.getIn(['timelines', `account:${props.params.accountId}`, 'next']),
-});
-
-@connect(mapStateToProps)
-export default class AccountTimeline extends ImmutablePureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    statusIds: ImmutablePropTypes.list,
-    isLoading: PropTypes.bool,
-    hasMore: PropTypes.bool,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchAccount(this.props.params.accountId));
-    this.props.dispatch(refreshAccountTimeline(this.props.params.accountId));
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) {
-      this.props.dispatch(fetchAccount(nextProps.params.accountId));
-      this.props.dispatch(refreshAccountTimeline(nextProps.params.accountId));
-    }
-  }
-
-  handleScrollToBottom = () => {
-    if (!this.props.isLoading && this.props.hasMore) {
-      this.props.dispatch(expandAccountTimeline(this.props.params.accountId));
-    }
-  }
-
-  render () {
-    const { statusIds, isLoading, hasMore } = this.props;
-
-    if (!statusIds && isLoading) {
-      return (
-        <Column>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
-    return (
-      <Column name='account'>
-        <ColumnBackButton />
-
-        <StatusList
-          prepend={<HeaderContainer accountId={this.props.params.accountId} />}
-          scrollKey='account_timeline'
-          statusIds={statusIds}
-          isLoading={isLoading}
-          hasMore={hasMore}
-          onScrollToBottom={this.handleScrollToBottom}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/blocks/index.js b/app/javascript/themes/glitch/features/blocks/index.js
deleted file mode 100644
index 70630818c..000000000
--- a/app/javascript/themes/glitch/features/blocks/index.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import LoadingIndicator from 'themes/glitch/components/loading_indicator';
-import { ScrollContainer } from 'react-router-scroll-4';
-import Column from 'themes/glitch/features/ui/components/column';
-import ColumnBackButtonSlim from 'themes/glitch/components/column_back_button_slim';
-import AccountContainer from 'themes/glitch/containers/account_container';
-import { fetchBlocks, expandBlocks } from 'themes/glitch/actions/blocks';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const messages = defineMessages({
-  heading: { id: 'column.blocks', defaultMessage: 'Blocked users' },
-});
-
-const mapStateToProps = state => ({
-  accountIds: state.getIn(['user_lists', 'blocks', 'items']),
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class Blocks extends ImmutablePureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    accountIds: ImmutablePropTypes.list,
-    intl: PropTypes.object.isRequired,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchBlocks());
-  }
-
-  handleScroll = (e) => {
-    const { scrollTop, scrollHeight, clientHeight } = e.target;
-
-    if (scrollTop === scrollHeight - clientHeight) {
-      this.props.dispatch(expandBlocks());
-    }
-  }
-
-  render () {
-    const { intl, accountIds } = this.props;
-
-    if (!accountIds) {
-      return (
-        <Column>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
-    return (
-      <Column name='blocks' icon='ban' heading={intl.formatMessage(messages.heading)}>
-        <ColumnBackButtonSlim />
-        <ScrollContainer scrollKey='blocks'>
-          <div className='scrollable' onScroll={this.handleScroll}>
-            {accountIds.map(id =>
-              <AccountContainer key={id} id={id} />
-            )}
-          </div>
-        </ScrollContainer>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/community_timeline/components/column_settings.js b/app/javascript/themes/glitch/features/community_timeline/components/column_settings.js
deleted file mode 100644
index 988e36308..000000000
--- a/app/javascript/themes/glitch/features/community_timeline/components/column_settings.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import SettingText from 'themes/glitch/components/setting_text';
-
-const messages = defineMessages({
-  filter_regex: { id: 'home.column_settings.filter_regex', defaultMessage: 'Filter out by regular expressions' },
-  settings: { id: 'home.settings', defaultMessage: 'Column settings' },
-});
-
-@injectIntl
-export default class ColumnSettings extends React.PureComponent {
-
-  static propTypes = {
-    settings: ImmutablePropTypes.map.isRequired,
-    onChange: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  render () {
-    const { settings, onChange, intl } = this.props;
-
-    return (
-      <div>
-        <span className='column-settings__section'><FormattedMessage id='home.column_settings.advanced' defaultMessage='Advanced' /></span>
-
-        <div className='column-settings__row'>
-          <SettingText settings={settings} settingKey={['regex', 'body']} onChange={onChange} label={intl.formatMessage(messages.filter_regex)} />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/community_timeline/containers/column_settings_container.js b/app/javascript/themes/glitch/features/community_timeline/containers/column_settings_container.js
deleted file mode 100644
index cd9c34365..000000000
--- a/app/javascript/themes/glitch/features/community_timeline/containers/column_settings_container.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { connect } from 'react-redux';
-import ColumnSettings from '../components/column_settings';
-import { changeSetting } from 'themes/glitch/actions/settings';
-
-const mapStateToProps = state => ({
-  settings: state.getIn(['settings', 'community']),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onChange (key, checked) {
-    dispatch(changeSetting(['community', ...key], checked));
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);
diff --git a/app/javascript/themes/glitch/features/community_timeline/index.js b/app/javascript/themes/glitch/features/community_timeline/index.js
deleted file mode 100644
index 9d255bd01..000000000
--- a/app/javascript/themes/glitch/features/community_timeline/index.js
+++ /dev/null
@@ -1,107 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import StatusListContainer from 'themes/glitch/features/ui/containers/status_list_container';
-import Column from 'themes/glitch/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-import {
-  refreshCommunityTimeline,
-  expandCommunityTimeline,
-} from 'themes/glitch/actions/timelines';
-import { addColumn, removeColumn, moveColumn } from 'themes/glitch/actions/columns';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import ColumnSettingsContainer from './containers/column_settings_container';
-import { connectCommunityStream } from 'themes/glitch/actions/streaming';
-
-const messages = defineMessages({
-  title: { id: 'column.community', defaultMessage: 'Local timeline' },
-});
-
-const mapStateToProps = state => ({
-  hasUnread: state.getIn(['timelines', 'community', 'unread']) > 0,
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class CommunityTimeline extends React.PureComponent {
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    columnId: PropTypes.string,
-    intl: PropTypes.object.isRequired,
-    hasUnread: PropTypes.bool,
-    multiColumn: PropTypes.bool,
-  };
-
-  handlePin = () => {
-    const { columnId, dispatch } = this.props;
-
-    if (columnId) {
-      dispatch(removeColumn(columnId));
-    } else {
-      dispatch(addColumn('COMMUNITY', {}));
-    }
-  }
-
-  handleMove = (dir) => {
-    const { columnId, dispatch } = this.props;
-    dispatch(moveColumn(columnId, dir));
-  }
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  componentDidMount () {
-    const { dispatch } = this.props;
-
-    dispatch(refreshCommunityTimeline());
-    this.disconnect = dispatch(connectCommunityStream());
-  }
-
-  componentWillUnmount () {
-    if (this.disconnect) {
-      this.disconnect();
-      this.disconnect = null;
-    }
-  }
-
-  setRef = c => {
-    this.column = c;
-  }
-
-  handleLoadMore = () => {
-    this.props.dispatch(expandCommunityTimeline());
-  }
-
-  render () {
-    const { intl, hasUnread, columnId, multiColumn } = this.props;
-    const pinned = !!columnId;
-
-    return (
-      <Column ref={this.setRef} name='local'>
-        <ColumnHeader
-          icon='users'
-          active={hasUnread}
-          title={intl.formatMessage(messages.title)}
-          onPin={this.handlePin}
-          onMove={this.handleMove}
-          onClick={this.handleHeaderClick}
-          pinned={pinned}
-          multiColumn={multiColumn}
-        >
-          <ColumnSettingsContainer />
-        </ColumnHeader>
-
-        <StatusListContainer
-          trackScroll={!pinned}
-          scrollKey={`community_timeline-${columnId}`}
-          timelineId='community'
-          loadMore={this.handleLoadMore}
-          emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/advanced_options.js b/app/javascript/themes/glitch/features/compose/components/advanced_options.js
deleted file mode 100644
index 045bad2e5..000000000
--- a/app/javascript/themes/glitch/features/compose/components/advanced_options.js
+++ /dev/null
@@ -1,62 +0,0 @@
-//  Package imports.
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { injectIntl, defineMessages } from 'react-intl';
-
-//  Our imports.
-import ComposeAdvancedOptionsToggle from './advanced_options_toggle';
-import ComposeDropdown from './dropdown';
-
-const messages = defineMessages({
-  local_only_short            :
-    { id: 'advanced-options.local-only.short', defaultMessage: 'Local-only' },
-  local_only_long             :
-    { id: 'advanced-options.local-only.long', defaultMessage: 'Do not post to other instances' },
-  advanced_options_icon_title :
-    { id: 'advanced_options.icon_title', defaultMessage: 'Advanced options' },
-});
-
-@injectIntl
-export default class ComposeAdvancedOptions extends React.PureComponent {
-
-  static propTypes = {
-    values   : ImmutablePropTypes.contains({
-      do_not_federate : PropTypes.bool.isRequired,
-    }).isRequired,
-    onChange : PropTypes.func.isRequired,
-    intl     : PropTypes.object.isRequired,
-  };
-
-  render () {
-    const { intl, values } = this.props;
-    const options = [
-      { icon: 'wifi', shortText: messages.local_only_short, longText: messages.local_only_long, name: 'do_not_federate' },
-    ];
-    const anyEnabled = values.some((enabled) => enabled);
-
-    const optionElems = options.map((option) => {
-      return (
-        <ComposeAdvancedOptionsToggle
-          onChange={this.props.onChange}
-          active={values.get(option.name)}
-          key={option.name}
-          name={option.name}
-          shortText={intl.formatMessage(option.shortText)}
-          longText={intl.formatMessage(option.longText)}
-        />
-      );
-    });
-
-    return (
-      <ComposeDropdown
-        title={intl.formatMessage(messages.advanced_options_icon_title)}
-        icon='home'
-        highlight={anyEnabled}
-      >
-        {optionElems}
-      </ComposeDropdown>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/advanced_options_toggle.js b/app/javascript/themes/glitch/features/compose/components/advanced_options_toggle.js
deleted file mode 100644
index 98b3b6a44..000000000
--- a/app/javascript/themes/glitch/features/compose/components/advanced_options_toggle.js
+++ /dev/null
@@ -1,35 +0,0 @@
-//  Package imports.
-import React from 'react';
-import PropTypes from 'prop-types';
-import Toggle from 'react-toggle';
-
-export default class ComposeAdvancedOptionsToggle extends React.PureComponent {
-
-  static propTypes = {
-    onChange: PropTypes.func.isRequired,
-    active: PropTypes.bool.isRequired,
-    name: PropTypes.string.isRequired,
-    shortText: PropTypes.string.isRequired,
-    longText: PropTypes.string.isRequired,
-  }
-
-  onToggle = () => {
-    this.props.onChange(this.props.name);
-  }
-
-  render() {
-    const { active, shortText, longText } = this.props;
-    return (
-      <div role='button' tabIndex='0' className='advanced-options-dropdown__option' onClick={this.onToggle}>
-        <div className='advanced-options-dropdown__option__toggle'>
-          <Toggle checked={active} onChange={this.onToggle} />
-        </div>
-        <div className='advanced-options-dropdown__option__content'>
-          <strong>{shortText}</strong>
-          {longText}
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/attach_options.js b/app/javascript/themes/glitch/features/compose/components/attach_options.js
deleted file mode 100644
index c396714f3..000000000
--- a/app/javascript/themes/glitch/features/compose/components/attach_options.js
+++ /dev/null
@@ -1,131 +0,0 @@
-//  Package imports  //
-import React from 'react';
-import PropTypes from 'prop-types';
-import { connect } from 'react-redux';
-import { injectIntl, defineMessages } from 'react-intl';
-
-//  Our imports  //
-import ComposeDropdown from './dropdown';
-import { uploadCompose } from 'themes/glitch/actions/compose';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { openModal } from 'themes/glitch/actions/modal';
-
-const messages = defineMessages({
-  upload :
-    { id: 'compose.attach.upload', defaultMessage: 'Upload a file' },
-  doodle :
-    { id: 'compose.attach.doodle', defaultMessage: 'Draw something' },
-  attach :
-    { id: 'compose.attach', defaultMessage: 'Attach...' },
-});
-
-const mapStateToProps = state => ({
-  // This horrible expression is copied from vanilla upload_button_container
-  disabled: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 3 || state.getIn(['compose', 'media_attachments']).some(m => m.get('type') === 'video')),
-  resetFileKey: state.getIn(['compose', 'resetFileKey']),
-  acceptContentTypes: state.getIn(['media_attachments', 'accept_content_types']),
-});
-
-const mapDispatchToProps = dispatch => ({
-  onSelectFile (files) {
-    dispatch(uploadCompose(files));
-  },
-  onOpenDoodle () {
-    dispatch(openModal('DOODLE', { noEsc: true }));
-  },
-});
-
-@injectIntl
-@connect(mapStateToProps, mapDispatchToProps)
-export default class ComposeAttachOptions extends ImmutablePureComponent {
-
-  static propTypes = {
-    intl     : PropTypes.object.isRequired,
-    resetFileKey: PropTypes.number,
-    acceptContentTypes: ImmutablePropTypes.listOf(PropTypes.string).isRequired,
-    disabled: PropTypes.bool,
-    onSelectFile: PropTypes.func.isRequired,
-    onOpenDoodle: PropTypes.func.isRequired,
-  };
-
-  handleItemClick = bt => {
-    if (bt === 'upload') {
-      this.fileElement.click();
-    }
-
-    if (bt === 'doodle') {
-      this.props.onOpenDoodle();
-    }
-
-    this.dropdown.setState({ open: false });
-  };
-
-  handleFileChange = (e) => {
-    if (e.target.files.length > 0) {
-      this.props.onSelectFile(e.target.files);
-    }
-  }
-
-  setFileRef = (c) => {
-    this.fileElement = c;
-  }
-
-  setDropdownRef = (c) => {
-    this.dropdown = c;
-  }
-
-  render () {
-    const { intl, resetFileKey, disabled, acceptContentTypes } = this.props;
-
-    const options = [
-      { icon: 'cloud-upload', text: messages.upload, name: 'upload' },
-      { icon: 'paint-brush', text: messages.doodle, name: 'doodle' },
-    ];
-
-    const optionElems = options.map((item) => {
-      const hdl = () => this.handleItemClick(item.name);
-      return (
-        <div
-          role='button'
-          tabIndex='0'
-          key={item.name}
-          onClick={hdl}
-          className='privacy-dropdown__option'
-        >
-          <div className='privacy-dropdown__option__icon'>
-            <i className={`fa fa-fw fa-${item.icon}`} />
-          </div>
-
-          <div className='privacy-dropdown__option__content'>
-            <strong>{intl.formatMessage(item.text)}</strong>
-          </div>
-        </div>
-      );
-    });
-
-    return (
-      <div>
-        <ComposeDropdown
-          title={intl.formatMessage(messages.attach)}
-          icon='paperclip'
-          disabled={disabled}
-          ref={this.setDropdownRef}
-        >
-          {optionElems}
-        </ComposeDropdown>
-        <input
-          key={resetFileKey}
-          ref={this.setFileRef}
-          type='file'
-          multiple={false}
-          accept={acceptContentTypes.toArray().join(',')}
-          onChange={this.handleFileChange}
-          disabled={disabled}
-          style={{ display: 'none' }}
-        />
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/autosuggest_account.js b/app/javascript/themes/glitch/features/compose/components/autosuggest_account.js
deleted file mode 100644
index 4a98d89fe..000000000
--- a/app/javascript/themes/glitch/features/compose/components/autosuggest_account.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import React from 'react';
-import Avatar from 'themes/glitch/components/avatar';
-import DisplayName from 'themes/glitch/components/display_name';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-export default class AutosuggestAccount extends ImmutablePureComponent {
-
-  static propTypes = {
-    account: ImmutablePropTypes.map.isRequired,
-  };
-
-  render () {
-    const { account } = this.props;
-
-    return (
-      <div className='autosuggest-account'>
-        <div className='autosuggest-account-icon'><Avatar account={account} size={18} /></div>
-        <DisplayName account={account} />
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/character_counter.js b/app/javascript/themes/glitch/features/compose/components/character_counter.js
deleted file mode 100644
index 0ecfc9141..000000000
--- a/app/javascript/themes/glitch/features/compose/components/character_counter.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { length } from 'stringz';
-
-export default class CharacterCounter extends React.PureComponent {
-
-  static propTypes = {
-    text: PropTypes.string.isRequired,
-    max: PropTypes.number.isRequired,
-  };
-
-  checkRemainingText (diff) {
-    if (diff < 0) {
-      return <span className='character-counter character-counter--over'>{diff}</span>;
-    }
-
-    return <span className='character-counter'>{diff}</span>;
-  }
-
-  render () {
-    const diff = this.props.max - length(this.props.text);
-    return this.checkRemainingText(diff);
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/compose_form.js b/app/javascript/themes/glitch/features/compose/components/compose_form.js
deleted file mode 100644
index 54b1944a4..000000000
--- a/app/javascript/themes/glitch/features/compose/components/compose_form.js
+++ /dev/null
@@ -1,286 +0,0 @@
-import React from 'react';
-import CharacterCounter from './character_counter';
-import Button from 'themes/glitch/components/button';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import ReplyIndicatorContainer from '../containers/reply_indicator_container';
-import AutosuggestTextarea from 'themes/glitch/components/autosuggest_textarea';
-import { defineMessages, injectIntl } from 'react-intl';
-import Collapsable from 'themes/glitch/components/collapsable';
-import SpoilerButtonContainer from '../containers/spoiler_button_container';
-import PrivacyDropdownContainer from '../containers/privacy_dropdown_container';
-import ComposeAdvancedOptionsContainer from '../containers/advanced_options_container';
-import SensitiveButtonContainer from '../containers/sensitive_button_container';
-import EmojiPickerDropdown from '../containers/emoji_picker_dropdown_container';
-import UploadFormContainer from '../containers/upload_form_container';
-import WarningContainer from '../containers/warning_container';
-import { isMobile } from 'themes/glitch/util/is_mobile';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { length } from 'stringz';
-import { countableText } from 'themes/glitch/util/counter';
-import ComposeAttachOptions from './attach_options';
-import initialState from 'themes/glitch/util/initial_state';
-
-const maxChars = initialState.max_toot_chars;
-
-const messages = defineMessages({
-  placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' },
-  spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here' },
-  publish: { id: 'compose_form.publish', defaultMessage: 'Toot' },
-  publishLoud: { id: 'compose_form.publish_loud', defaultMessage: '{publish}!' },
-});
-
-@injectIntl
-export default class ComposeForm extends ImmutablePureComponent {
-
-  static propTypes = {
-    intl: PropTypes.object.isRequired,
-    text: PropTypes.string.isRequired,
-    suggestion_token: PropTypes.string,
-    suggestions: ImmutablePropTypes.list,
-    spoiler: PropTypes.bool,
-    privacy: PropTypes.string,
-    advanced_options: ImmutablePropTypes.contains({
-      do_not_federate: PropTypes.bool,
-    }),
-    spoiler_text: PropTypes.string,
-    focusDate: PropTypes.instanceOf(Date),
-    preselectDate: PropTypes.instanceOf(Date),
-    is_submitting: PropTypes.bool,
-    is_uploading: PropTypes.bool,
-    onChange: PropTypes.func.isRequired,
-    onSubmit: PropTypes.func.isRequired,
-    onClearSuggestions: PropTypes.func.isRequired,
-    onFetchSuggestions: PropTypes.func.isRequired,
-    onPrivacyChange: PropTypes.func.isRequired,
-    onSuggestionSelected: PropTypes.func.isRequired,
-    onChangeSpoilerText: PropTypes.func.isRequired,
-    onPaste: PropTypes.func.isRequired,
-    onPickEmoji: PropTypes.func.isRequired,
-    showSearch: PropTypes.bool,
-    settings : ImmutablePropTypes.map.isRequired,
-  };
-
-  static defaultProps = {
-    showSearch: false,
-  };
-
-  handleChange = (e) => {
-    this.props.onChange(e.target.value);
-  }
-
-  handleKeyDown = (e) => {
-    if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
-      this.handleSubmit();
-    }
-  }
-
-  handleSubmit2 = () => {
-    this.props.onPrivacyChange(this.props.settings.get('side_arm'));
-    this.handleSubmit();
-  }
-
-  handleSubmit = () => {
-    if (this.props.text !== this.autosuggestTextarea.textarea.value) {
-      // Something changed the text inside the textarea (e.g. browser extensions like Grammarly)
-      // Update the state to match the current text
-      this.props.onChange(this.autosuggestTextarea.textarea.value);
-    }
-
-    this.props.onSubmit();
-  }
-
-  onSuggestionsClearRequested = () => {
-    this.props.onClearSuggestions();
-  }
-
-  onSuggestionsFetchRequested = (token) => {
-    this.props.onFetchSuggestions(token);
-  }
-
-  onSuggestionSelected = (tokenStart, token, value) => {
-    this._restoreCaret = null;
-    this.props.onSuggestionSelected(tokenStart, token, value);
-  }
-
-  handleChangeSpoilerText = (e) => {
-    this.props.onChangeSpoilerText(e.target.value);
-  }
-
-  componentWillReceiveProps (nextProps) {
-    // If this is the update where we've finished uploading,
-    // save the last caret position so we can restore it below!
-    if (!nextProps.is_uploading && this.props.is_uploading) {
-      this._restoreCaret = this.autosuggestTextarea.textarea.selectionStart;
-    }
-  }
-
-  componentDidUpdate (prevProps) {
-    // This statement does several things:
-    // - If we're beginning a reply, and,
-    //     - Replying to zero or one users, places the cursor at the end of the textbox.
-    //     - Replying to more than one user, selects any usernames past the first;
-    //       this provides a convenient shortcut to drop everyone else from the conversation.
-    // - If we've just finished uploading an image, and have a saved caret position,
-    //   restores the cursor to that position after the text changes!
-    if (this.props.focusDate !== prevProps.focusDate || (prevProps.is_uploading && !this.props.is_uploading && typeof this._restoreCaret === 'number')) {
-      let selectionEnd, selectionStart;
-
-      if (this.props.preselectDate !== prevProps.preselectDate) {
-        selectionEnd   = this.props.text.length;
-        selectionStart = this.props.text.search(/\s/) + 1;
-      } else if (typeof this._restoreCaret === 'number') {
-        selectionStart = this._restoreCaret;
-        selectionEnd   = this._restoreCaret;
-      } else {
-        selectionEnd   = this.props.text.length;
-        selectionStart = selectionEnd;
-      }
-
-      this.autosuggestTextarea.textarea.setSelectionRange(selectionStart, selectionEnd);
-      this.autosuggestTextarea.textarea.focus();
-    } else if(prevProps.is_submitting && !this.props.is_submitting) {
-      this.autosuggestTextarea.textarea.focus();
-    }
-  }
-
-  setAutosuggestTextarea = (c) => {
-    this.autosuggestTextarea = c;
-  }
-
-  handleEmojiPick = (data) => {
-    const position     = this.autosuggestTextarea.textarea.selectionStart;
-    const emojiChar    = data.native;
-    this._restoreCaret = position + emojiChar.length + 1;
-    this.props.onPickEmoji(position, data);
-  }
-
-  render () {
-    const { intl, onPaste, showSearch } = this.props;
-    const disabled = this.props.is_submitting;
-    const maybeEye = (this.props.advanced_options && this.props.advanced_options.do_not_federate) ? ' 👁️' : '';
-    const text     = [this.props.spoiler_text, countableText(this.props.text), maybeEye].join('');
-
-    const secondaryVisibility = this.props.settings.get('side_arm');
-    let showSideArm = secondaryVisibility !== 'none';
-
-    let publishText = '';
-    let publishText2 = '';
-    let title = '';
-    let title2 = '';
-
-    const privacyIcons = {
-      none: '',
-      public: 'globe',
-      unlisted: 'unlock-alt',
-      private: 'lock',
-      direct: 'envelope',
-    };
-
-    title = `${intl.formatMessage(messages.publish)}: ${intl.formatMessage({ id: `privacy.${this.props.privacy}.short` })}`;
-
-    if (showSideArm) {
-      // Enhanced behavior with dual toot buttons
-      publishText = (
-        <span>
-          {
-            <i
-              className={`fa fa-${privacyIcons[this.props.privacy]}`}
-              style={{ paddingRight: '5px' }}
-            />
-          }{intl.formatMessage(messages.publish)}
-        </span>
-      );
-
-      title2 = `${intl.formatMessage(messages.publish)}: ${intl.formatMessage({ id: `privacy.${secondaryVisibility}.short` })}`;
-      publishText2 = (
-        <i
-          className={`fa fa-${privacyIcons[secondaryVisibility]}`}
-          aria-label={title2}
-        />
-      );
-    } else {
-      // Original vanilla behavior - no icon if public or unlisted
-      if (this.props.privacy === 'private' || this.props.privacy === 'direct') {
-        publishText = <span className='compose-form__publish-private'><i className='fa fa-lock' /> {intl.formatMessage(messages.publish)}</span>;
-      } else {
-        publishText = this.props.privacy !== 'unlisted' ? intl.formatMessage(messages.publishLoud, { publish: intl.formatMessage(messages.publish) }) : intl.formatMessage(messages.publish);
-      }
-    }
-
-    const submitDisabled = disabled || this.props.is_uploading || length(text) > maxChars || (text.length !== 0 && text.trim().length === 0);
-
-    return (
-      <div className='compose-form'>
-        <Collapsable isVisible={this.props.spoiler} fullHeight={50}>
-          <div className='spoiler-input'>
-            <label>
-              <span style={{ display: 'none' }}>{intl.formatMessage(messages.spoiler_placeholder)}</span>
-              <input placeholder={intl.formatMessage(messages.spoiler_placeholder)} value={this.props.spoiler_text} onChange={this.handleChangeSpoilerText} onKeyDown={this.handleKeyDown} type='text' className='spoiler-input__input'  id='cw-spoiler-input' />
-            </label>
-          </div>
-        </Collapsable>
-
-        <WarningContainer />
-
-        <ReplyIndicatorContainer />
-
-        <div className='compose-form__autosuggest-wrapper'>
-          <AutosuggestTextarea
-            ref={this.setAutosuggestTextarea}
-            placeholder={intl.formatMessage(messages.placeholder)}
-            disabled={disabled}
-            value={this.props.text}
-            onChange={this.handleChange}
-            suggestions={this.props.suggestions}
-            onKeyDown={this.handleKeyDown}
-            onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
-            onSuggestionsClearRequested={this.onSuggestionsClearRequested}
-            onSuggestionSelected={this.onSuggestionSelected}
-            onPaste={onPaste}
-            autoFocus={!showSearch && !isMobile(window.innerWidth)}
-          />
-
-          <EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
-        </div>
-
-        <div className='compose-form__modifiers'>
-          <UploadFormContainer />
-        </div>
-
-        <div className='compose-form__buttons'>
-          <ComposeAttachOptions />
-          <SensitiveButtonContainer />
-          <div className='compose-form__buttons-separator' />
-          <PrivacyDropdownContainer />
-          <SpoilerButtonContainer />
-          <ComposeAdvancedOptionsContainer />
-        </div>
-
-        <div className='compose-form__publish'>
-          <div className='character-counter__wrapper'><CharacterCounter max={maxChars} text={text} /></div>
-          <div className='compose-form__publish-button-wrapper'>
-            {
-              showSideArm ?
-                <Button
-                  className='compose-form__publish__side-arm'
-                  text={publishText2}
-                  title={title2}
-                  onClick={this.handleSubmit2}
-                  disabled={submitDisabled}
-                /> : ''
-            }
-            <Button
-              className='compose-form__publish__primary'
-              text={publishText}
-              title={title}
-              onClick={this.handleSubmit}
-              disabled={submitDisabled}
-            />
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/dropdown.js b/app/javascript/themes/glitch/features/compose/components/dropdown.js
deleted file mode 100644
index f3d9f094e..000000000
--- a/app/javascript/themes/glitch/features/compose/components/dropdown.js
+++ /dev/null
@@ -1,77 +0,0 @@
-//  Package imports.
-import React from 'react';
-import PropTypes from 'prop-types';
-
-//  Our imports.
-import IconButton from 'themes/glitch/components/icon_button';
-
-const iconStyle = {
-  height     : null,
-  lineHeight : '27px',
-};
-
-export default class ComposeDropdown extends React.PureComponent {
-
-  static propTypes = {
-    title: PropTypes.string.isRequired,
-    icon: PropTypes.string,
-    highlight: PropTypes.bool,
-    disabled: PropTypes.bool,
-    children: PropTypes.arrayOf(PropTypes.node).isRequired,
-  };
-
-  state = {
-    open: false,
-  };
-
-  onGlobalClick = (e) => {
-    if (e.target !== this.node && !this.node.contains(e.target) && this.state.open) {
-      this.setState({ open: false });
-    }
-  };
-
-  componentDidMount () {
-    window.addEventListener('click', this.onGlobalClick);
-    window.addEventListener('touchstart', this.onGlobalClick);
-  }
-  componentWillUnmount () {
-    window.removeEventListener('click', this.onGlobalClick);
-    window.removeEventListener('touchstart', this.onGlobalClick);
-  }
-
-  onToggleDropdown = () => {
-    if (this.props.disabled) return;
-    this.setState({ open: !this.state.open });
-  };
-
-  setRef = (c) => {
-    this.node = c;
-  };
-
-  render () {
-    const { open } = this.state;
-    let { highlight, title, icon, disabled } = this.props;
-
-    if (!icon) icon = 'ellipsis-h';
-
-    return (
-      <div ref={this.setRef} className={`advanced-options-dropdown ${open ?  'open' : ''} ${highlight ? 'active' : ''} `}>
-        <div className='advanced-options-dropdown__value'>
-          <IconButton
-            className={'inverted'}
-            title={title}
-            icon={icon} active={open || highlight}
-            size={18}
-            style={iconStyle}
-            disabled={disabled}
-            onClick={this.onToggleDropdown}
-          />
-        </div>
-        <div className='advanced-options-dropdown__dropdown'>
-          {this.props.children}
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/emoji_picker_dropdown.js b/app/javascript/themes/glitch/features/compose/components/emoji_picker_dropdown.js
deleted file mode 100644
index fd59efb85..000000000
--- a/app/javascript/themes/glitch/features/compose/components/emoji_picker_dropdown.js
+++ /dev/null
@@ -1,376 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl } from 'react-intl';
-import { EmojiPicker as EmojiPickerAsync } from 'themes/glitch/util/async-components';
-import Overlay from 'react-overlays/lib/Overlay';
-import classNames from 'classnames';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import detectPassiveEvents from 'detect-passive-events';
-import { buildCustomEmojis } from 'themes/glitch/util/emoji';
-
-const messages = defineMessages({
-  emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' },
-  emoji_search: { id: 'emoji_button.search', defaultMessage: 'Search...' },
-  emoji_not_found: { id: 'emoji_button.not_found', defaultMessage: 'No emojos!! (╯°□°)╯︵ ┻━┻' },
-  custom: { id: 'emoji_button.custom', defaultMessage: 'Custom' },
-  recent: { id: 'emoji_button.recent', defaultMessage: 'Frequently used' },
-  search_results: { id: 'emoji_button.search_results', defaultMessage: 'Search results' },
-  people: { id: 'emoji_button.people', defaultMessage: 'People' },
-  nature: { id: 'emoji_button.nature', defaultMessage: 'Nature' },
-  food: { id: 'emoji_button.food', defaultMessage: 'Food & Drink' },
-  activity: { id: 'emoji_button.activity', defaultMessage: 'Activity' },
-  travel: { id: 'emoji_button.travel', defaultMessage: 'Travel & Places' },
-  objects: { id: 'emoji_button.objects', defaultMessage: 'Objects' },
-  symbols: { id: 'emoji_button.symbols', defaultMessage: 'Symbols' },
-  flags: { id: 'emoji_button.flags', defaultMessage: 'Flags' },
-});
-
-const assetHost = process.env.CDN_HOST || '';
-let EmojiPicker, Emoji; // load asynchronously
-
-const backgroundImageFn = () => `${assetHost}/emoji/sheet.png`;
-const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false;
-
-const categoriesSort = [
-  'recent',
-  'custom',
-  'people',
-  'nature',
-  'foods',
-  'activity',
-  'places',
-  'objects',
-  'symbols',
-  'flags',
-];
-
-class ModifierPickerMenu extends React.PureComponent {
-
-  static propTypes = {
-    active: PropTypes.bool,
-    onSelect: PropTypes.func.isRequired,
-    onClose: PropTypes.func.isRequired,
-  };
-
-  handleClick = e => {
-    this.props.onSelect(e.currentTarget.getAttribute('data-index') * 1);
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (nextProps.active) {
-      this.attachListeners();
-    } else {
-      this.removeListeners();
-    }
-  }
-
-  componentWillUnmount () {
-    this.removeListeners();
-  }
-
-  handleDocumentClick = e => {
-    if (this.node && !this.node.contains(e.target)) {
-      this.props.onClose();
-    }
-  }
-
-  attachListeners () {
-    document.addEventListener('click', this.handleDocumentClick, false);
-    document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
-  }
-
-  removeListeners () {
-    document.removeEventListener('click', this.handleDocumentClick, false);
-    document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
-  }
-
-  setRef = c => {
-    this.node = c;
-  }
-
-  render () {
-    const { active } = this.props;
-
-    return (
-      <div className='emoji-picker-dropdown__modifiers__menu' style={{ display: active ? 'block' : 'none' }} ref={this.setRef}>
-        <button onClick={this.handleClick} data-index={1}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={1} backgroundImageFn={backgroundImageFn} /></button>
-        <button onClick={this.handleClick} data-index={2}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={2} backgroundImageFn={backgroundImageFn} /></button>
-        <button onClick={this.handleClick} data-index={3}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={3} backgroundImageFn={backgroundImageFn} /></button>
-        <button onClick={this.handleClick} data-index={4}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={4} backgroundImageFn={backgroundImageFn} /></button>
-        <button onClick={this.handleClick} data-index={5}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={5} backgroundImageFn={backgroundImageFn} /></button>
-        <button onClick={this.handleClick} data-index={6}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={6} backgroundImageFn={backgroundImageFn} /></button>
-      </div>
-    );
-  }
-
-}
-
-class ModifierPicker extends React.PureComponent {
-
-  static propTypes = {
-    active: PropTypes.bool,
-    modifier: PropTypes.number,
-    onChange: PropTypes.func,
-    onClose: PropTypes.func,
-    onOpen: PropTypes.func,
-  };
-
-  handleClick = () => {
-    if (this.props.active) {
-      this.props.onClose();
-    } else {
-      this.props.onOpen();
-    }
-  }
-
-  handleSelect = modifier => {
-    this.props.onChange(modifier);
-    this.props.onClose();
-  }
-
-  render () {
-    const { active, modifier } = this.props;
-
-    return (
-      <div className='emoji-picker-dropdown__modifiers'>
-        <Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={modifier} onClick={this.handleClick} backgroundImageFn={backgroundImageFn} />
-        <ModifierPickerMenu active={active} onSelect={this.handleSelect} onClose={this.props.onClose} />
-      </div>
-    );
-  }
-
-}
-
-@injectIntl
-class EmojiPickerMenu extends React.PureComponent {
-
-  static propTypes = {
-    custom_emojis: ImmutablePropTypes.list,
-    frequentlyUsedEmojis: PropTypes.arrayOf(PropTypes.string),
-    loading: PropTypes.bool,
-    onClose: PropTypes.func.isRequired,
-    onPick: PropTypes.func.isRequired,
-    style: PropTypes.object,
-    placement: PropTypes.string,
-    arrowOffsetLeft: PropTypes.string,
-    arrowOffsetTop: PropTypes.string,
-    intl: PropTypes.object.isRequired,
-    skinTone: PropTypes.number.isRequired,
-    onSkinTone: PropTypes.func.isRequired,
-  };
-
-  static defaultProps = {
-    style: {},
-    loading: true,
-    placement: 'bottom',
-    frequentlyUsedEmojis: [],
-  };
-
-  state = {
-    modifierOpen: false,
-  };
-
-  handleDocumentClick = e => {
-    if (this.node && !this.node.contains(e.target)) {
-      this.props.onClose();
-    }
-  }
-
-  componentDidMount () {
-    document.addEventListener('click', this.handleDocumentClick, false);
-    document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
-  }
-
-  componentWillUnmount () {
-    document.removeEventListener('click', this.handleDocumentClick, false);
-    document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
-  }
-
-  setRef = c => {
-    this.node = c;
-  }
-
-  getI18n = () => {
-    const { intl } = this.props;
-
-    return {
-      search: intl.formatMessage(messages.emoji_search),
-      notfound: intl.formatMessage(messages.emoji_not_found),
-      categories: {
-        search: intl.formatMessage(messages.search_results),
-        recent: intl.formatMessage(messages.recent),
-        people: intl.formatMessage(messages.people),
-        nature: intl.formatMessage(messages.nature),
-        foods: intl.formatMessage(messages.food),
-        activity: intl.formatMessage(messages.activity),
-        places: intl.formatMessage(messages.travel),
-        objects: intl.formatMessage(messages.objects),
-        symbols: intl.formatMessage(messages.symbols),
-        flags: intl.formatMessage(messages.flags),
-        custom: intl.formatMessage(messages.custom),
-      },
-    };
-  }
-
-  handleClick = emoji => {
-    if (!emoji.native) {
-      emoji.native = emoji.colons;
-    }
-
-    this.props.onClose();
-    this.props.onPick(emoji);
-  }
-
-  handleModifierOpen = () => {
-    this.setState({ modifierOpen: true });
-  }
-
-  handleModifierClose = () => {
-    this.setState({ modifierOpen: false });
-  }
-
-  handleModifierChange = modifier => {
-    this.props.onSkinTone(modifier);
-  }
-
-  render () {
-    const { loading, style, intl, custom_emojis, skinTone, frequentlyUsedEmojis } = this.props;
-
-    if (loading) {
-      return <div style={{ width: 299 }} />;
-    }
-
-    const title = intl.formatMessage(messages.emoji);
-    const { modifierOpen } = this.state;
-
-    return (
-      <div className={classNames('emoji-picker-dropdown__menu', { selecting: modifierOpen })} style={style} ref={this.setRef}>
-        <EmojiPicker
-          perLine={8}
-          emojiSize={22}
-          sheetSize={32}
-          custom={buildCustomEmojis(custom_emojis)}
-          color=''
-          emoji=''
-          set='twitter'
-          title={title}
-          i18n={this.getI18n()}
-          onClick={this.handleClick}
-          include={categoriesSort}
-          recent={frequentlyUsedEmojis}
-          skin={skinTone}
-          showPreview={false}
-          backgroundImageFn={backgroundImageFn}
-          emojiTooltip
-        />
-
-        <ModifierPicker
-          active={modifierOpen}
-          modifier={skinTone}
-          onOpen={this.handleModifierOpen}
-          onClose={this.handleModifierClose}
-          onChange={this.handleModifierChange}
-        />
-      </div>
-    );
-  }
-
-}
-
-@injectIntl
-export default class EmojiPickerDropdown extends React.PureComponent {
-
-  static propTypes = {
-    custom_emojis: ImmutablePropTypes.list,
-    frequentlyUsedEmojis: PropTypes.arrayOf(PropTypes.string),
-    intl: PropTypes.object.isRequired,
-    onPickEmoji: PropTypes.func.isRequired,
-    onSkinTone: PropTypes.func.isRequired,
-    skinTone: PropTypes.number.isRequired,
-  };
-
-  state = {
-    active: false,
-    loading: false,
-  };
-
-  setRef = (c) => {
-    this.dropdown = c;
-  }
-
-  onShowDropdown = () => {
-    this.setState({ active: true });
-
-    if (!EmojiPicker) {
-      this.setState({ loading: true });
-
-      EmojiPickerAsync().then(EmojiMart => {
-        EmojiPicker = EmojiMart.Picker;
-        Emoji       = EmojiMart.Emoji;
-
-        this.setState({ loading: false });
-      }).catch(() => {
-        this.setState({ loading: false });
-      });
-    }
-  }
-
-  onHideDropdown = () => {
-    this.setState({ active: false });
-  }
-
-  onToggle = (e) => {
-    if (!this.state.loading && (!e.key || e.key === 'Enter')) {
-      if (this.state.active) {
-        this.onHideDropdown();
-      } else {
-        this.onShowDropdown();
-      }
-    }
-  }
-
-  handleKeyDown = e => {
-    if (e.key === 'Escape') {
-      this.onHideDropdown();
-    }
-  }
-
-  setTargetRef = c => {
-    this.target = c;
-  }
-
-  findTarget = () => {
-    return this.target;
-  }
-
-  render () {
-    const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis } = this.props;
-    const title = intl.formatMessage(messages.emoji);
-    const { active, loading } = this.state;
-
-    return (
-      <div className='emoji-picker-dropdown' onKeyDown={this.handleKeyDown}>
-        <div ref={this.setTargetRef} className='emoji-button' title={title} aria-label={title} aria-expanded={active} role='button' onClick={this.onToggle} onKeyDown={this.onToggle} tabIndex={0}>
-          <img
-            className={classNames('emojione', { 'pulse-loading': active && loading })}
-            alt='🙂'
-            src={`${assetHost}/emoji/1f602.svg`}
-          />
-        </div>
-
-        <Overlay show={active} placement='bottom' target={this.findTarget}>
-          <EmojiPickerMenu
-            custom_emojis={this.props.custom_emojis}
-            loading={loading}
-            onClose={this.onHideDropdown}
-            onPick={onPickEmoji}
-            onSkinTone={onSkinTone}
-            skinTone={skinTone}
-            frequentlyUsedEmojis={frequentlyUsedEmojis}
-          />
-        </Overlay>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/navigation_bar.js b/app/javascript/themes/glitch/features/compose/components/navigation_bar.js
deleted file mode 100644
index 24a70949b..000000000
--- a/app/javascript/themes/glitch/features/compose/components/navigation_bar.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import Avatar from 'themes/glitch/components/avatar';
-import IconButton from 'themes/glitch/components/icon_button';
-import Permalink from 'themes/glitch/components/permalink';
-import { FormattedMessage } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-export default class NavigationBar extends ImmutablePureComponent {
-
-  static propTypes = {
-    account: ImmutablePropTypes.map.isRequired,
-    onClose: PropTypes.func.isRequired,
-  };
-
-  render () {
-    return (
-      <div className='navigation-bar'>
-        <Permalink href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}>
-          <span style={{ display: 'none' }}>{this.props.account.get('acct')}</span>
-          <Avatar account={this.props.account} size={40} />
-        </Permalink>
-
-        <div className='navigation-bar__profile'>
-          <Permalink href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}>
-            <strong className='navigation-bar__profile-account'>@{this.props.account.get('acct')}</strong>
-          </Permalink>
-
-          <a href='/settings/profile' className='navigation-bar__profile-edit'><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a>
-        </div>
-
-        <IconButton title='' icon='close' onClick={this.props.onClose} />
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/privacy_dropdown.js b/app/javascript/themes/glitch/features/compose/components/privacy_dropdown.js
deleted file mode 100644
index 0cd92d174..000000000
--- a/app/javascript/themes/glitch/features/compose/components/privacy_dropdown.js
+++ /dev/null
@@ -1,200 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { injectIntl, defineMessages } from 'react-intl';
-import IconButton from 'themes/glitch/components/icon_button';
-import Overlay from 'react-overlays/lib/Overlay';
-import Motion from 'themes/glitch/util/optional_motion';
-import spring from 'react-motion/lib/spring';
-import detectPassiveEvents from 'detect-passive-events';
-import classNames from 'classnames';
-
-const messages = defineMessages({
-  public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
-  public_long: { id: 'privacy.public.long', defaultMessage: 'Post to public timelines' },
-  unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
-  unlisted_long: { id: 'privacy.unlisted.long', defaultMessage: 'Do not show in public timelines' },
-  private_short: { id: 'privacy.private.short', defaultMessage: 'Followers-only' },
-  private_long: { id: 'privacy.private.long', defaultMessage: 'Post to followers only' },
-  direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
-  direct_long: { id: 'privacy.direct.long', defaultMessage: 'Post to mentioned users only' },
-  change_privacy: { id: 'privacy.change', defaultMessage: 'Adjust status privacy' },
-});
-
-const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false;
-
-class PrivacyDropdownMenu extends React.PureComponent {
-
-  static propTypes = {
-    style: PropTypes.object,
-    items: PropTypes.array.isRequired,
-    value: PropTypes.string.isRequired,
-    onClose: PropTypes.func.isRequired,
-    onChange: PropTypes.func.isRequired,
-  };
-
-  handleDocumentClick = e => {
-    if (this.node && !this.node.contains(e.target)) {
-      this.props.onClose();
-    }
-  }
-
-  handleClick = e => {
-    if (e.key === 'Escape') {
-      this.props.onClose();
-    } else if (!e.key || e.key === 'Enter') {
-      const value = e.currentTarget.getAttribute('data-index');
-
-      e.preventDefault();
-
-      this.props.onClose();
-      this.props.onChange(value);
-    }
-  }
-
-  componentDidMount () {
-    document.addEventListener('click', this.handleDocumentClick, false);
-    document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
-  }
-
-  componentWillUnmount () {
-    document.removeEventListener('click', this.handleDocumentClick, false);
-    document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
-  }
-
-  setRef = c => {
-    this.node = c;
-  }
-
-  render () {
-    const { style, items, value } = this.props;
-
-    return (
-      <Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
-        {({ opacity, scaleX, scaleY }) => (
-          <div className='privacy-dropdown__dropdown' style={{ ...style, opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }} ref={this.setRef}>
-            {items.map(item =>
-              <div role='button' tabIndex='0' key={item.value} data-index={item.value} onKeyDown={this.handleClick} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })}>
-                <div className='privacy-dropdown__option__icon'>
-                  <i className={`fa fa-fw fa-${item.icon}`} />
-                </div>
-
-                <div className='privacy-dropdown__option__content'>
-                  <strong>{item.text}</strong>
-                  {item.meta}
-                </div>
-              </div>
-            )}
-          </div>
-        )}
-      </Motion>
-    );
-  }
-
-}
-
-@injectIntl
-export default class PrivacyDropdown extends React.PureComponent {
-
-  static propTypes = {
-    isUserTouching: PropTypes.func,
-    isModalOpen: PropTypes.bool.isRequired,
-    onModalOpen: PropTypes.func,
-    onModalClose: PropTypes.func,
-    value: PropTypes.string.isRequired,
-    onChange: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  state = {
-    open: false,
-  };
-
-  handleToggle = () => {
-    if (this.props.isUserTouching()) {
-      if (this.state.open) {
-        this.props.onModalClose();
-      } else {
-        this.props.onModalOpen({
-          actions: this.options.map(option => ({ ...option, active: option.value === this.props.value })),
-          onClick: this.handleModalActionClick,
-        });
-      }
-    } else {
-      this.setState({ open: !this.state.open });
-    }
-  }
-
-  handleModalActionClick = (e) => {
-    e.preventDefault();
-
-    const { value } = this.options[e.currentTarget.getAttribute('data-index')];
-
-    this.props.onModalClose();
-    this.props.onChange(value);
-  }
-
-  handleKeyDown = e => {
-    switch(e.key) {
-    case 'Enter':
-      this.handleToggle();
-      break;
-    case 'Escape':
-      this.handleClose();
-      break;
-    }
-  }
-
-  handleClose = () => {
-    this.setState({ open: false });
-  }
-
-  handleChange = value => {
-    this.props.onChange(value);
-  }
-
-  componentWillMount () {
-    const { intl: { formatMessage } } = this.props;
-
-    this.options = [
-      { icon: 'globe', value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) },
-      { icon: 'unlock-alt', value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long) },
-      { icon: 'lock', value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) },
-      { icon: 'envelope', value: 'direct', text: formatMessage(messages.direct_short), meta: formatMessage(messages.direct_long) },
-    ];
-  }
-
-  render () {
-    const { value, intl } = this.props;
-    const { open } = this.state;
-
-    const valueOption = this.options.find(item => item.value === value);
-
-    return (
-      <div className={classNames('privacy-dropdown', { active: open })} onKeyDown={this.handleKeyDown}>
-        <div className={classNames('privacy-dropdown__value', { active: this.options.indexOf(valueOption) === 0 })}>
-          <IconButton
-            className='privacy-dropdown__value-icon'
-            icon={valueOption.icon}
-            title={intl.formatMessage(messages.change_privacy)}
-            size={18}
-            expanded={open}
-            active={open}
-            inverted
-            onClick={this.handleToggle}
-            style={{ height: null, lineHeight: '27px' }}
-          />
-        </div>
-
-        <Overlay show={open} placement='bottom' target={this}>
-          <PrivacyDropdownMenu
-            items={this.options}
-            value={value}
-            onClose={this.handleClose}
-            onChange={this.handleChange}
-          />
-        </Overlay>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/reply_indicator.js b/app/javascript/themes/glitch/features/compose/components/reply_indicator.js
deleted file mode 100644
index 9a8d10ceb..000000000
--- a/app/javascript/themes/glitch/features/compose/components/reply_indicator.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import Avatar from 'themes/glitch/components/avatar';
-import IconButton from 'themes/glitch/components/icon_button';
-import DisplayName from 'themes/glitch/components/display_name';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const messages = defineMessages({
-  cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' },
-});
-
-@injectIntl
-export default class ReplyIndicator extends ImmutablePureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  static propTypes = {
-    status: ImmutablePropTypes.map,
-    onCancel: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  handleClick = () => {
-    this.props.onCancel();
-  }
-
-  handleAccountClick = (e) => {
-    if (e.button === 0) {
-      e.preventDefault();
-      this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
-    }
-  }
-
-  render () {
-    const { status, intl } = this.props;
-
-    if (!status) {
-      return null;
-    }
-
-    const content  = { __html: status.get('contentHtml') };
-
-    return (
-      <div className='reply-indicator'>
-        <div className='reply-indicator__header'>
-          <div className='reply-indicator__cancel'><IconButton title={intl.formatMessage(messages.cancel)} icon='times' onClick={this.handleClick} /></div>
-
-          <a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='reply-indicator__display-name'>
-            <div className='reply-indicator__display-avatar'><Avatar account={status.get('account')} size={24} /></div>
-            <DisplayName account={status.get('account')} />
-          </a>
-        </div>
-
-        <div className='reply-indicator__content' dangerouslySetInnerHTML={content} />
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/search.js b/app/javascript/themes/glitch/features/compose/components/search.js
deleted file mode 100644
index c3218137f..000000000
--- a/app/javascript/themes/glitch/features/compose/components/search.js
+++ /dev/null
@@ -1,129 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import Overlay from 'react-overlays/lib/Overlay';
-import Motion from 'themes/glitch/util/optional_motion';
-import spring from 'react-motion/lib/spring';
-
-const messages = defineMessages({
-  placeholder: { id: 'search.placeholder', defaultMessage: 'Search' },
-});
-
-class SearchPopout extends React.PureComponent {
-
-  static propTypes = {
-    style: PropTypes.object,
-  };
-
-  render () {
-    const { style } = this.props;
-
-    return (
-      <div style={{ ...style, position: 'absolute', width: 285 }}>
-        <Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
-          {({ opacity, scaleX, scaleY }) => (
-            <div className='search-popout' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
-              <h4><FormattedMessage id='search_popout.search_format' defaultMessage='Advanced search format' /></h4>
-
-              <ul>
-                <li><em>#example</em> <FormattedMessage id='search_popout.tips.hashtag' defaultMessage='hashtag' /></li>
-                <li><em>@username@domain</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
-                <li><em>URL</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
-                <li><em>URL</em> <FormattedMessage id='search_popout.tips.status' defaultMessage='status' /></li>
-              </ul>
-
-              <FormattedMessage id='search_popout.tips.text' defaultMessage='Simple text returns matching display names, usernames and hashtags' />
-            </div>
-          )}
-        </Motion>
-      </div>
-    );
-  }
-
-}
-
-@injectIntl
-export default class Search extends React.PureComponent {
-
-  static propTypes = {
-    value: PropTypes.string.isRequired,
-    submitted: PropTypes.bool,
-    onChange: PropTypes.func.isRequired,
-    onSubmit: PropTypes.func.isRequired,
-    onClear: PropTypes.func.isRequired,
-    onShow: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  state = {
-    expanded: false,
-  };
-
-  handleChange = (e) => {
-    this.props.onChange(e.target.value);
-  }
-
-  handleClear = (e) => {
-    e.preventDefault();
-
-    if (this.props.value.length > 0 || this.props.submitted) {
-      this.props.onClear();
-    }
-  }
-
-  handleKeyDown = (e) => {
-    if (e.key === 'Enter') {
-      e.preventDefault();
-      this.props.onSubmit();
-    } else if (e.key === 'Escape') {
-      document.querySelector('.ui').parentElement.focus();
-    }
-  }
-
-  noop () {
-
-  }
-
-  handleFocus = () => {
-    this.setState({ expanded: true });
-    this.props.onShow();
-  }
-
-  handleBlur = () => {
-    this.setState({ expanded: false });
-  }
-
-  render () {
-    const { intl, value, submitted } = this.props;
-    const { expanded } = this.state;
-    const hasValue = value.length > 0 || submitted;
-
-    return (
-      <div className='search'>
-        <label>
-          <span style={{ display: 'none' }}>{intl.formatMessage(messages.placeholder)}</span>
-          <input
-            className='search__input'
-            type='text'
-            placeholder={intl.formatMessage(messages.placeholder)}
-            value={value}
-            onChange={this.handleChange}
-            onKeyUp={this.handleKeyDown}
-            onFocus={this.handleFocus}
-            onBlur={this.handleBlur}
-          />
-        </label>
-
-        <div role='button' tabIndex='0' className='search__icon' onClick={this.handleClear}>
-          <i className={`fa fa-search ${hasValue ? '' : 'active'}`} />
-          <i aria-label={intl.formatMessage(messages.placeholder)} className={`fa fa-times-circle ${hasValue ? 'active' : ''}`} />
-        </div>
-
-        <Overlay show={expanded && !hasValue} placement='bottom' target={this}>
-          <SearchPopout />
-        </Overlay>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/search_results.js b/app/javascript/themes/glitch/features/compose/components/search_results.js
deleted file mode 100644
index 3fdafa5f3..000000000
--- a/app/javascript/themes/glitch/features/compose/components/search_results.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { FormattedMessage } from 'react-intl';
-import AccountContainer from 'themes/glitch/containers/account_container';
-import StatusContainer from 'themes/glitch/containers/status_container';
-import { Link } from 'react-router-dom';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-export default class SearchResults extends ImmutablePureComponent {
-
-  static propTypes = {
-    results: ImmutablePropTypes.map.isRequired,
-  };
-
-  render () {
-    const { results } = this.props;
-
-    let accounts, statuses, hashtags;
-    let count = 0;
-
-    if (results.get('accounts') && results.get('accounts').size > 0) {
-      count   += results.get('accounts').size;
-      accounts = (
-        <div className='search-results__section'>
-          {results.get('accounts').map(accountId => <AccountContainer key={accountId} id={accountId} />)}
-        </div>
-      );
-    }
-
-    if (results.get('statuses') && results.get('statuses').size > 0) {
-      count   += results.get('statuses').size;
-      statuses = (
-        <div className='search-results__section'>
-          {results.get('statuses').map(statusId => <StatusContainer key={statusId} id={statusId} />)}
-        </div>
-      );
-    }
-
-    if (results.get('hashtags') && results.get('hashtags').size > 0) {
-      count += results.get('hashtags').size;
-      hashtags = (
-        <div className='search-results__section'>
-          {results.get('hashtags').map(hashtag =>
-            <Link key={hashtag} className='search-results__hashtag' to={`/timelines/tag/${hashtag}`}>
-              #{hashtag}
-            </Link>
-          )}
-        </div>
-      );
-    }
-
-    return (
-      <div className='search-results'>
-        <div className='search-results__header'>
-          <FormattedMessage id='search_results.total' defaultMessage='{count, number} {count, plural, one {result} other {results}}' values={{ count }} />
-        </div>
-
-        {accounts}
-        {statuses}
-        {hashtags}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/text_icon_button.js b/app/javascript/themes/glitch/features/compose/components/text_icon_button.js
deleted file mode 100644
index 9c8ffab1f..000000000
--- a/app/javascript/themes/glitch/features/compose/components/text_icon_button.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-export default class TextIconButton extends React.PureComponent {
-
-  static propTypes = {
-    label: PropTypes.string.isRequired,
-    title: PropTypes.string,
-    active: PropTypes.bool,
-    onClick: PropTypes.func.isRequired,
-    ariaControls: PropTypes.string,
-  };
-
-  handleClick = (e) => {
-    e.preventDefault();
-    this.props.onClick();
-  }
-
-  render () {
-    const { label, title, active, ariaControls } = this.props;
-
-    return (
-      <button title={title} aria-label={title} className={`text-icon-button ${active ? 'active' : ''}`} aria-expanded={active} onClick={this.handleClick} aria-controls={ariaControls}>
-        {label}
-      </button>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/upload.js b/app/javascript/themes/glitch/features/compose/components/upload.js
deleted file mode 100644
index ded376ada..000000000
--- a/app/javascript/themes/glitch/features/compose/components/upload.js
+++ /dev/null
@@ -1,96 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import IconButton from 'themes/glitch/components/icon_button';
-import Motion from 'themes/glitch/util/optional_motion';
-import spring from 'react-motion/lib/spring';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { defineMessages, injectIntl } from 'react-intl';
-import classNames from 'classnames';
-
-const messages = defineMessages({
-  undo: { id: 'upload_form.undo', defaultMessage: 'Undo' },
-  description: { id: 'upload_form.description', defaultMessage: 'Describe for the visually impaired' },
-});
-
-@injectIntl
-export default class Upload extends ImmutablePureComponent {
-
-  static propTypes = {
-    media: ImmutablePropTypes.map.isRequired,
-    intl: PropTypes.object.isRequired,
-    onUndo: PropTypes.func.isRequired,
-    onDescriptionChange: PropTypes.func.isRequired,
-  };
-
-  state = {
-    hovered: false,
-    focused: false,
-    dirtyDescription: null,
-  };
-
-  handleUndoClick = () => {
-    this.props.onUndo(this.props.media.get('id'));
-  }
-
-  handleInputChange = e => {
-    this.setState({ dirtyDescription: e.target.value });
-  }
-
-  handleMouseEnter = () => {
-    this.setState({ hovered: true });
-  }
-
-  handleMouseLeave = () => {
-    this.setState({ hovered: false });
-  }
-
-  handleInputFocus = () => {
-    this.setState({ focused: true });
-  }
-
-  handleInputBlur = () => {
-    const { dirtyDescription } = this.state;
-
-    this.setState({ focused: false, dirtyDescription: null });
-
-    if (dirtyDescription !== null) {
-      this.props.onDescriptionChange(this.props.media.get('id'), dirtyDescription);
-    }
-  }
-
-  render () {
-    const { intl, media } = this.props;
-    const active          = this.state.hovered || this.state.focused;
-    const description     = this.state.dirtyDescription || media.get('description') || '';
-
-    return (
-      <div className='compose-form__upload' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
-        <Motion defaultStyle={{ scale: 0.8 }} style={{ scale: spring(1, { stiffness: 180, damping: 12 }) }}>
-          {({ scale }) => (
-            <div className='compose-form__upload-thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: `url(${media.get('preview_url')})` }}>
-              <IconButton icon='times' title={intl.formatMessage(messages.undo)} size={36} onClick={this.handleUndoClick} />
-
-              <div className={classNames('compose-form__upload-description', { active })}>
-                <label>
-                  <span style={{ display: 'none' }}>{intl.formatMessage(messages.description)}</span>
-
-                  <input
-                    placeholder={intl.formatMessage(messages.description)}
-                    type='text'
-                    value={description}
-                    maxLength={420}
-                    onFocus={this.handleInputFocus}
-                    onChange={this.handleInputChange}
-                    onBlur={this.handleInputBlur}
-                  />
-                </label>
-              </div>
-            </div>
-          )}
-        </Motion>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/upload_button.js b/app/javascript/themes/glitch/features/compose/components/upload_button.js
deleted file mode 100644
index d7742adfe..000000000
--- a/app/javascript/themes/glitch/features/compose/components/upload_button.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import React from 'react';
-import IconButton from 'themes/glitch/components/icon_button';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl } from 'react-intl';
-import { connect } from 'react-redux';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-
-const messages = defineMessages({
-  upload: { id: 'upload_button.label', defaultMessage: 'Add media' },
-});
-
-const makeMapStateToProps = () => {
-  const mapStateToProps = state => ({
-    acceptContentTypes: state.getIn(['media_attachments', 'accept_content_types']),
-  });
-
-  return mapStateToProps;
-};
-
-const iconStyle = {
-  height: null,
-  lineHeight: '27px',
-};
-
-@connect(makeMapStateToProps)
-@injectIntl
-export default class UploadButton extends ImmutablePureComponent {
-
-  static propTypes = {
-    disabled: PropTypes.bool,
-    onSelectFile: PropTypes.func.isRequired,
-    style: PropTypes.object,
-    resetFileKey: PropTypes.number,
-    acceptContentTypes: ImmutablePropTypes.listOf(PropTypes.string).isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  handleChange = (e) => {
-    if (e.target.files.length > 0) {
-      this.props.onSelectFile(e.target.files);
-    }
-  }
-
-  handleClick = () => {
-    this.fileElement.click();
-  }
-
-  setRef = (c) => {
-    this.fileElement = c;
-  }
-
-  render () {
-
-    const { intl, resetFileKey, disabled, acceptContentTypes } = this.props;
-
-    return (
-      <div className='compose-form__upload-button'>
-        <IconButton icon='camera' title={intl.formatMessage(messages.upload)} disabled={disabled} onClick={this.handleClick} className='compose-form__upload-button-icon' size={18} inverted style={iconStyle} />
-        <label>
-          <span style={{ display: 'none' }}>{intl.formatMessage(messages.upload)}</span>
-          <input
-            key={resetFileKey}
-            ref={this.setRef}
-            type='file'
-            multiple={false}
-            accept={acceptContentTypes.toArray().join(',')}
-            onChange={this.handleChange}
-            disabled={disabled}
-            style={{ display: 'none' }}
-          />
-        </label>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/upload_form.js b/app/javascript/themes/glitch/features/compose/components/upload_form.js
deleted file mode 100644
index b7f112205..000000000
--- a/app/javascript/themes/glitch/features/compose/components/upload_form.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import UploadProgressContainer from '../containers/upload_progress_container';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import UploadContainer from '../containers/upload_container';
-
-export default class UploadForm extends ImmutablePureComponent {
-
-  static propTypes = {
-    mediaIds: ImmutablePropTypes.list.isRequired,
-  };
-
-  render () {
-    const { mediaIds } = this.props;
-
-    return (
-      <div className='compose-form__upload-wrapper'>
-        <UploadProgressContainer />
-
-        <div className='compose-form__uploads-wrapper'>
-          {mediaIds.map(id => (
-            <UploadContainer id={id} key={id} />
-          ))}
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/upload_progress.js b/app/javascript/themes/glitch/features/compose/components/upload_progress.js
deleted file mode 100644
index b923d0a22..000000000
--- a/app/javascript/themes/glitch/features/compose/components/upload_progress.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Motion from 'themes/glitch/util/optional_motion';
-import spring from 'react-motion/lib/spring';
-import { FormattedMessage } from 'react-intl';
-
-export default class UploadProgress extends React.PureComponent {
-
-  static propTypes = {
-    active: PropTypes.bool,
-    progress: PropTypes.number,
-  };
-
-  render () {
-    const { active, progress } = this.props;
-
-    if (!active) {
-      return null;
-    }
-
-    return (
-      <div className='upload-progress'>
-        <div className='upload-progress__icon'>
-          <i className='fa fa-upload' />
-        </div>
-
-        <div className='upload-progress__message'>
-          <FormattedMessage id='upload_progress.label' defaultMessage='Uploading...' />
-
-          <div className='upload-progress__backdrop'>
-            <Motion defaultStyle={{ width: 0 }} style={{ width: spring(progress) }}>
-              {({ width }) =>
-                <div className='upload-progress__tracker' style={{ width: `${width}%` }} />
-              }
-            </Motion>
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/components/warning.js b/app/javascript/themes/glitch/features/compose/components/warning.js
deleted file mode 100644
index 82df55a31..000000000
--- a/app/javascript/themes/glitch/features/compose/components/warning.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Motion from 'themes/glitch/util/optional_motion';
-import spring from 'react-motion/lib/spring';
-
-export default class Warning extends React.PureComponent {
-
-  static propTypes = {
-    message: PropTypes.node.isRequired,
-  };
-
-  render () {
-    const { message } = this.props;
-
-    return (
-      <Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
-        {({ opacity, scaleX, scaleY }) => (
-          <div className='compose-form__warning' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
-            {message}
-          </div>
-        )}
-      </Motion>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/compose/containers/advanced_options_container.js b/app/javascript/themes/glitch/features/compose/containers/advanced_options_container.js
deleted file mode 100644
index 9f168942a..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/advanced_options_container.js
+++ /dev/null
@@ -1,20 +0,0 @@
-//  Package imports.
-import { connect } from 'react-redux';
-
-//  Our imports.
-import { toggleComposeAdvancedOption } from 'themes/glitch/actions/compose';
-import ComposeAdvancedOptions from '../components/advanced_options';
-
-const mapStateToProps = state => ({
-  values: state.getIn(['compose', 'advanced_options']),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onChange (option) {
-    dispatch(toggleComposeAdvancedOption(option));
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(ComposeAdvancedOptions);
diff --git a/app/javascript/themes/glitch/features/compose/containers/autosuggest_account_container.js b/app/javascript/themes/glitch/features/compose/containers/autosuggest_account_container.js
deleted file mode 100644
index 96eb70c18..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/autosuggest_account_container.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { connect } from 'react-redux';
-import AutosuggestAccount from '../components/autosuggest_account';
-import { makeGetAccount } from 'themes/glitch/selectors';
-
-const makeMapStateToProps = () => {
-  const getAccount = makeGetAccount();
-
-  const mapStateToProps = (state, { id }) => ({
-    account: getAccount(state, id),
-  });
-
-  return mapStateToProps;
-};
-
-export default connect(makeMapStateToProps)(AutosuggestAccount);
diff --git a/app/javascript/themes/glitch/features/compose/containers/compose_form_container.js b/app/javascript/themes/glitch/features/compose/containers/compose_form_container.js
deleted file mode 100644
index 7afa988f1..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/compose_form_container.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import { connect } from 'react-redux';
-import ComposeForm from '../components/compose_form';
-import { changeComposeVisibility, uploadCompose } from 'themes/glitch/actions/compose';
-import {
-  changeCompose,
-  submitCompose,
-  clearComposeSuggestions,
-  fetchComposeSuggestions,
-  selectComposeSuggestion,
-  changeComposeSpoilerText,
-  insertEmojiCompose,
-} from 'themes/glitch/actions/compose';
-
-const mapStateToProps = state => ({
-  text: state.getIn(['compose', 'text']),
-  suggestion_token: state.getIn(['compose', 'suggestion_token']),
-  suggestions: state.getIn(['compose', 'suggestions']),
-  advanced_options: state.getIn(['compose', 'advanced_options']),
-  spoiler: state.getIn(['compose', 'spoiler']),
-  spoiler_text: state.getIn(['compose', 'spoiler_text']),
-  privacy: state.getIn(['compose', 'privacy']),
-  focusDate: state.getIn(['compose', 'focusDate']),
-  preselectDate: state.getIn(['compose', 'preselectDate']),
-  is_submitting: state.getIn(['compose', 'is_submitting']),
-  is_uploading: state.getIn(['compose', 'is_uploading']),
-  showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
-  settings: state.get('local_settings'),
-  filesAttached: state.getIn(['compose', 'media_attachments']).size > 0,
-});
-
-const mapDispatchToProps = (dispatch) => ({
-
-  onChange (text) {
-    dispatch(changeCompose(text));
-  },
-
-  onPrivacyChange (value) {
-    dispatch(changeComposeVisibility(value));
-  },
-
-  onSubmit () {
-    dispatch(submitCompose());
-  },
-
-  onClearSuggestions () {
-    dispatch(clearComposeSuggestions());
-  },
-
-  onFetchSuggestions (token) {
-    dispatch(fetchComposeSuggestions(token));
-  },
-
-  onSuggestionSelected (position, token, accountId) {
-    dispatch(selectComposeSuggestion(position, token, accountId));
-  },
-
-  onChangeSpoilerText (checked) {
-    dispatch(changeComposeSpoilerText(checked));
-  },
-
-  onPaste (files) {
-    dispatch(uploadCompose(files));
-  },
-
-  onPickEmoji (position, data) {
-    dispatch(insertEmojiCompose(position, data));
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(ComposeForm);
diff --git a/app/javascript/themes/glitch/features/compose/containers/emoji_picker_dropdown_container.js b/app/javascript/themes/glitch/features/compose/containers/emoji_picker_dropdown_container.js
deleted file mode 100644
index 55a13bd65..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/emoji_picker_dropdown_container.js
+++ /dev/null
@@ -1,82 +0,0 @@
-import { connect } from 'react-redux';
-import EmojiPickerDropdown from '../components/emoji_picker_dropdown';
-import { changeSetting } from 'themes/glitch/actions/settings';
-import { createSelector } from 'reselect';
-import { Map as ImmutableMap } from 'immutable';
-import { useEmoji } from 'themes/glitch/actions/emojis';
-
-const perLine = 8;
-const lines   = 2;
-
-const DEFAULTS = [
-  '+1',
-  'grinning',
-  'kissing_heart',
-  'heart_eyes',
-  'laughing',
-  'stuck_out_tongue_winking_eye',
-  'sweat_smile',
-  'joy',
-  'yum',
-  'disappointed',
-  'thinking_face',
-  'weary',
-  'sob',
-  'sunglasses',
-  'heart',
-  'ok_hand',
-];
-
-const getFrequentlyUsedEmojis = createSelector([
-  state => state.getIn(['settings', 'frequentlyUsedEmojis'], ImmutableMap()),
-], emojiCounters => {
-  let emojis = emojiCounters
-    .keySeq()
-    .sort((a, b) => emojiCounters.get(a) - emojiCounters.get(b))
-    .reverse()
-    .slice(0, perLine * lines)
-    .toArray();
-
-  if (emojis.length < DEFAULTS.length) {
-    emojis = emojis.concat(DEFAULTS.slice(0, DEFAULTS.length - emojis.length));
-  }
-
-  return emojis;
-});
-
-const getCustomEmojis = createSelector([
-  state => state.get('custom_emojis'),
-], emojis => emojis.filter(e => e.get('visible_in_picker')).sort((a, b) => {
-  const aShort = a.get('shortcode').toLowerCase();
-  const bShort = b.get('shortcode').toLowerCase();
-
-  if (aShort < bShort) {
-    return -1;
-  } else if (aShort > bShort ) {
-    return 1;
-  } else {
-    return 0;
-  }
-}));
-
-const mapStateToProps = state => ({
-  custom_emojis: getCustomEmojis(state),
-  skinTone: state.getIn(['settings', 'skinTone']),
-  frequentlyUsedEmojis: getFrequentlyUsedEmojis(state),
-});
-
-const mapDispatchToProps = (dispatch, { onPickEmoji }) => ({
-  onSkinTone: skinTone => {
-    dispatch(changeSetting(['skinTone'], skinTone));
-  },
-
-  onPickEmoji: emoji => {
-    dispatch(useEmoji(emoji));
-
-    if (onPickEmoji) {
-      onPickEmoji(emoji);
-    }
-  },
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(EmojiPickerDropdown);
diff --git a/app/javascript/themes/glitch/features/compose/containers/navigation_container.js b/app/javascript/themes/glitch/features/compose/containers/navigation_container.js
deleted file mode 100644
index b6d737b46..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/navigation_container.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import { connect }   from 'react-redux';
-import NavigationBar from '../components/navigation_bar';
-import { me } from 'themes/glitch/util/initial_state';
-
-const mapStateToProps = state => {
-  return {
-    account: state.getIn(['accounts', me]),
-  };
-};
-
-export default connect(mapStateToProps)(NavigationBar);
diff --git a/app/javascript/themes/glitch/features/compose/containers/privacy_dropdown_container.js b/app/javascript/themes/glitch/features/compose/containers/privacy_dropdown_container.js
deleted file mode 100644
index 9636ceab2..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/privacy_dropdown_container.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import { connect } from 'react-redux';
-import PrivacyDropdown from '../components/privacy_dropdown';
-import { changeComposeVisibility } from 'themes/glitch/actions/compose';
-import { openModal, closeModal } from 'themes/glitch/actions/modal';
-import { isUserTouching } from 'themes/glitch/util/is_mobile';
-
-const mapStateToProps = state => ({
-  isModalOpen: state.get('modal').modalType === 'ACTIONS',
-  value: state.getIn(['compose', 'privacy']),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onChange (value) {
-    dispatch(changeComposeVisibility(value));
-  },
-
-  isUserTouching,
-  onModalOpen: props => dispatch(openModal('ACTIONS', props)),
-  onModalClose: () => dispatch(closeModal()),
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(PrivacyDropdown);
diff --git a/app/javascript/themes/glitch/features/compose/containers/reply_indicator_container.js b/app/javascript/themes/glitch/features/compose/containers/reply_indicator_container.js
deleted file mode 100644
index 6dcabb3cd..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/reply_indicator_container.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import { connect } from 'react-redux';
-import { cancelReplyCompose } from 'themes/glitch/actions/compose';
-import { makeGetStatus } from 'themes/glitch/selectors';
-import ReplyIndicator from '../components/reply_indicator';
-
-const makeMapStateToProps = () => {
-  const getStatus = makeGetStatus();
-
-  const mapStateToProps = state => ({
-    status: getStatus(state, state.getIn(['compose', 'in_reply_to'])),
-  });
-
-  return mapStateToProps;
-};
-
-const mapDispatchToProps = dispatch => ({
-
-  onCancel () {
-    dispatch(cancelReplyCompose());
-  },
-
-});
-
-export default connect(makeMapStateToProps, mapDispatchToProps)(ReplyIndicator);
diff --git a/app/javascript/themes/glitch/features/compose/containers/search_container.js b/app/javascript/themes/glitch/features/compose/containers/search_container.js
deleted file mode 100644
index a450d27e7..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/search_container.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import { connect } from 'react-redux';
-import {
-  changeSearch,
-  clearSearch,
-  submitSearch,
-  showSearch,
-} from 'themes/glitch/actions/search';
-import Search from '../components/search';
-
-const mapStateToProps = state => ({
-  value: state.getIn(['search', 'value']),
-  submitted: state.getIn(['search', 'submitted']),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onChange (value) {
-    dispatch(changeSearch(value));
-  },
-
-  onClear () {
-    dispatch(clearSearch());
-  },
-
-  onSubmit () {
-    dispatch(submitSearch());
-  },
-
-  onShow () {
-    dispatch(showSearch());
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(Search);
diff --git a/app/javascript/themes/glitch/features/compose/containers/search_results_container.js b/app/javascript/themes/glitch/features/compose/containers/search_results_container.js
deleted file mode 100644
index 16d95d417..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/search_results_container.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { connect } from 'react-redux';
-import SearchResults from '../components/search_results';
-
-const mapStateToProps = state => ({
-  results: state.getIn(['search', 'results']),
-});
-
-export default connect(mapStateToProps)(SearchResults);
diff --git a/app/javascript/themes/glitch/features/compose/containers/sensitive_button_container.js b/app/javascript/themes/glitch/features/compose/containers/sensitive_button_container.js
deleted file mode 100644
index a710dd104..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/sensitive_button_container.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-import IconButton from 'themes/glitch/components/icon_button';
-import { changeComposeSensitivity } from 'themes/glitch/actions/compose';
-import Motion from 'themes/glitch/util/optional_motion';
-import spring from 'react-motion/lib/spring';
-import { injectIntl, defineMessages } from 'react-intl';
-
-const messages = defineMessages({
-  title: { id: 'compose_form.sensitive', defaultMessage: 'Mark media as sensitive' },
-});
-
-const mapStateToProps = state => ({
-  visible: state.getIn(['compose', 'media_attachments']).size > 0,
-  active: state.getIn(['compose', 'sensitive']),
-  disabled: state.getIn(['compose', 'spoiler']),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onClick () {
-    dispatch(changeComposeSensitivity());
-  },
-
-});
-
-class SensitiveButton extends React.PureComponent {
-
-  static propTypes = {
-    visible: PropTypes.bool,
-    active: PropTypes.bool,
-    disabled: PropTypes.bool,
-    onClick: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  render () {
-    const { visible, active, disabled, onClick, intl } = this.props;
-
-    return (
-      <Motion defaultStyle={{ scale: 0.87 }} style={{ scale: spring(visible ? 1 : 0.87, { stiffness: 200, damping: 3 }) }}>
-        {({ scale }) => {
-          const icon = active ? 'eye-slash' : 'eye';
-          const className = classNames('compose-form__sensitive-button', {
-            'compose-form__sensitive-button--visible': visible,
-          });
-          return (
-            <div className={className} style={{ transform: `scale(${scale})` }}>
-              <IconButton
-                className='compose-form__sensitive-button__icon'
-                title={intl.formatMessage(messages.title)}
-                icon={icon}
-                onClick={onClick}
-                size={18}
-                active={active}
-                disabled={disabled}
-                style={{ lineHeight: null, height: null }}
-                inverted
-              />
-            </div>
-          );
-        }}
-      </Motion>
-    );
-  }
-
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(SensitiveButton));
diff --git a/app/javascript/themes/glitch/features/compose/containers/spoiler_button_container.js b/app/javascript/themes/glitch/features/compose/containers/spoiler_button_container.js
deleted file mode 100644
index 160e71ba9..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/spoiler_button_container.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { connect } from 'react-redux';
-import TextIconButton from '../components/text_icon_button';
-import { changeComposeSpoilerness } from 'themes/glitch/actions/compose';
-import { injectIntl, defineMessages } from 'react-intl';
-
-const messages = defineMessages({
-  title: { id: 'compose_form.spoiler', defaultMessage: 'Hide text behind warning' },
-});
-
-const mapStateToProps = (state, { intl }) => ({
-  label: 'CW',
-  title: intl.formatMessage(messages.title),
-  active: state.getIn(['compose', 'spoiler']),
-  ariaControls: 'cw-spoiler-input',
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onClick () {
-    dispatch(changeComposeSpoilerness());
-  },
-
-});
-
-export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(TextIconButton));
diff --git a/app/javascript/themes/glitch/features/compose/containers/upload_button_container.js b/app/javascript/themes/glitch/features/compose/containers/upload_button_container.js
deleted file mode 100644
index f332eae1a..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/upload_button_container.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { connect } from 'react-redux';
-import UploadButton from '../components/upload_button';
-import { uploadCompose } from 'themes/glitch/actions/compose';
-
-const mapStateToProps = state => ({
-  disabled: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 3 || state.getIn(['compose', 'media_attachments']).some(m => m.get('type') === 'video')),
-  resetFileKey: state.getIn(['compose', 'resetFileKey']),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onSelectFile (files) {
-    dispatch(uploadCompose(files));
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(UploadButton);
diff --git a/app/javascript/themes/glitch/features/compose/containers/upload_container.js b/app/javascript/themes/glitch/features/compose/containers/upload_container.js
deleted file mode 100644
index eea514bf5..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/upload_container.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { connect } from 'react-redux';
-import Upload from '../components/upload';
-import { undoUploadCompose, changeUploadCompose } from 'themes/glitch/actions/compose';
-
-const mapStateToProps = (state, { id }) => ({
-  media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onUndo: id => {
-    dispatch(undoUploadCompose(id));
-  },
-
-  onDescriptionChange: (id, description) => {
-    dispatch(changeUploadCompose(id, description));
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(Upload);
diff --git a/app/javascript/themes/glitch/features/compose/containers/upload_form_container.js b/app/javascript/themes/glitch/features/compose/containers/upload_form_container.js
deleted file mode 100644
index a6798bf51..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/upload_form_container.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { connect } from 'react-redux';
-import UploadForm from '../components/upload_form';
-
-const mapStateToProps = state => ({
-  mediaIds: state.getIn(['compose', 'media_attachments']).map(item => item.get('id')),
-});
-
-export default connect(mapStateToProps)(UploadForm);
diff --git a/app/javascript/themes/glitch/features/compose/containers/upload_progress_container.js b/app/javascript/themes/glitch/features/compose/containers/upload_progress_container.js
deleted file mode 100644
index 0cfee96da..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/upload_progress_container.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { connect } from 'react-redux';
-import UploadProgress from '../components/upload_progress';
-
-const mapStateToProps = state => ({
-  active: state.getIn(['compose', 'is_uploading']),
-  progress: state.getIn(['compose', 'progress']),
-});
-
-export default connect(mapStateToProps)(UploadProgress);
diff --git a/app/javascript/themes/glitch/features/compose/containers/warning_container.js b/app/javascript/themes/glitch/features/compose/containers/warning_container.js
deleted file mode 100644
index 225d6a1dd..000000000
--- a/app/javascript/themes/glitch/features/compose/containers/warning_container.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import Warning from '../components/warning';
-import PropTypes from 'prop-types';
-import { FormattedMessage } from 'react-intl';
-import { me } from 'themes/glitch/util/initial_state';
-
-const mapStateToProps = state => ({
-  needsLockWarning: state.getIn(['compose', 'privacy']) === 'private' && !state.getIn(['accounts', me, 'locked']),
-});
-
-const WarningWrapper = ({ needsLockWarning }) => {
-  if (needsLockWarning) {
-    return <Warning message={<FormattedMessage id='compose_form.lock_disclaimer' defaultMessage='Your account is not {locked}. Anyone can follow you to view your follower-only posts.' values={{ locked: <a href='/settings/profile'><FormattedMessage id='compose_form.lock_disclaimer.lock' defaultMessage='locked' /></a> }} />} />;
-  }
-
-  return null;
-};
-
-WarningWrapper.propTypes = {
-  needsLockWarning: PropTypes.bool,
-};
-
-export default connect(mapStateToProps)(WarningWrapper);
diff --git a/app/javascript/themes/glitch/features/compose/index.js b/app/javascript/themes/glitch/features/compose/index.js
deleted file mode 100644
index 3fcaf416f..000000000
--- a/app/javascript/themes/glitch/features/compose/index.js
+++ /dev/null
@@ -1,126 +0,0 @@
-import React from 'react';
-import ComposeFormContainer from './containers/compose_form_container';
-import NavigationContainer from './containers/navigation_container';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { connect } from 'react-redux';
-import { mountCompose, unmountCompose } from 'themes/glitch/actions/compose';
-import { openModal } from 'themes/glitch/actions/modal';
-import { changeLocalSetting } from 'themes/glitch/actions/local_settings';
-import { Link } from 'react-router-dom';
-import { injectIntl, defineMessages } from 'react-intl';
-import SearchContainer from './containers/search_container';
-import Motion from 'themes/glitch/util/optional_motion';
-import spring from 'react-motion/lib/spring';
-import SearchResultsContainer from './containers/search_results_container';
-import { changeComposing } from 'themes/glitch/actions/compose';
-
-const messages = defineMessages({
-  start: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
-  home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' },
-  notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
-  public: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' },
-  community: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
-  settings: { id: 'navigation_bar.app_settings', defaultMessage: 'App settings' },
-  logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
-});
-
-const mapStateToProps = state => ({
-  columns: state.getIn(['settings', 'columns']),
-  showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class Compose extends React.PureComponent {
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    columns: ImmutablePropTypes.list.isRequired,
-    multiColumn: PropTypes.bool,
-    showSearch: PropTypes.bool,
-    intl: PropTypes.object.isRequired,
-  };
-
-  componentDidMount () {
-    this.props.dispatch(mountCompose());
-  }
-
-  componentWillUnmount () {
-    this.props.dispatch(unmountCompose());
-  }
-
-  onLayoutClick = (e) => {
-    const layout = e.currentTarget.getAttribute('data-mastodon-layout');
-    this.props.dispatch(changeLocalSetting(['layout'], layout));
-    e.preventDefault();
-  }
-
-  openSettings = () => {
-    this.props.dispatch(openModal('SETTINGS', {}));
-  }
-
-  onFocus = () => {
-    this.props.dispatch(changeComposing(true));
-  }
-
-  onBlur = () => {
-    this.props.dispatch(changeComposing(false));
-  }
-
-  render () {
-    const { multiColumn, showSearch, intl } = this.props;
-
-    let header = '';
-
-    if (multiColumn) {
-      const { columns } = this.props;
-      header = (
-        <nav className='drawer__header'>
-          <Link to='/getting-started' className='drawer__tab' title={intl.formatMessage(messages.start)} aria-label={intl.formatMessage(messages.start)}><i role='img' className='fa fa-fw fa-asterisk' /></Link>
-          {!columns.some(column => column.get('id') === 'HOME') && (
-            <Link to='/timelines/home' className='drawer__tab' title={intl.formatMessage(messages.home_timeline)} aria-label={intl.formatMessage(messages.home_timeline)}><i role='img' className='fa fa-fw fa-home' /></Link>
-          )}
-          {!columns.some(column => column.get('id') === 'NOTIFICATIONS') && (
-            <Link to='/notifications' className='drawer__tab' title={intl.formatMessage(messages.notifications)} aria-label={intl.formatMessage(messages.notifications)}><i role='img' className='fa fa-fw fa-bell' /></Link>
-          )}
-          {!columns.some(column => column.get('id') === 'COMMUNITY') && (
-            <Link to='/timelines/public/local' className='drawer__tab' title={intl.formatMessage(messages.community)} aria-label={intl.formatMessage(messages.community)}><i role='img' className='fa fa-fw fa-users' /></Link>
-          )}
-          {!columns.some(column => column.get('id') === 'PUBLIC') && (
-            <Link to='/timelines/public' className='drawer__tab' title={intl.formatMessage(messages.public)} aria-label={intl.formatMessage(messages.public)}><i role='img' className='fa fa-fw fa-globe' /></Link>
-          )}
-          <a onClick={this.openSettings} role='button' tabIndex='0' className='drawer__tab' title={intl.formatMessage(messages.settings)} aria-label={intl.formatMessage(messages.settings)}><i role='img' className='fa fa-fw fa-cogs' /></a>
-          <a href='/auth/sign_out' className='drawer__tab' data-method='delete' title={intl.formatMessage(messages.logout)} aria-label={intl.formatMessage(messages.logout)}><i role='img' className='fa fa-fw fa-sign-out' /></a>
-        </nav>
-      );
-    }
-
-
-
-    return (
-      <div className='drawer'>
-        {header}
-
-        <SearchContainer />
-
-        <div className='drawer__pager'>
-          <div className='drawer__inner scrollable optionally-scrollable' onFocus={this.onFocus}>
-            <NavigationContainer onClose={this.onBlur} />
-            <ComposeFormContainer />
-          </div>
-
-          <Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
-            {({ x }) =>
-              <div className='drawer__inner darker' style={{ transform: `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}>
-                <SearchResultsContainer />
-              </div>
-            }
-          </Motion>
-        </div>
-
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/direct_timeline/containers/column_settings_container.js b/app/javascript/themes/glitch/features/direct_timeline/containers/column_settings_container.js
deleted file mode 100644
index 2a40c65a5..000000000
--- a/app/javascript/themes/glitch/features/direct_timeline/containers/column_settings_container.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { connect } from 'react-redux';
-import ColumnSettings from 'themes/glitch/features/community_timeline/components/column_settings';
-import { changeSetting } from 'themes/glitch/actions/settings';
-
-const mapStateToProps = state => ({
-  settings: state.getIn(['settings', 'direct']),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onChange (key, checked) {
-    dispatch(changeSetting(['direct', ...key], checked));
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);
diff --git a/app/javascript/themes/glitch/features/direct_timeline/index.js b/app/javascript/themes/glitch/features/direct_timeline/index.js
deleted file mode 100644
index 6b29cf94d..000000000
--- a/app/javascript/themes/glitch/features/direct_timeline/index.js
+++ /dev/null
@@ -1,107 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import StatusListContainer from 'themes/glitch/features/ui/containers/status_list_container';
-import Column from 'themes/glitch/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-import {
-  refreshDirectTimeline,
-  expandDirectTimeline,
-} from 'themes/glitch/actions/timelines';
-import { addColumn, removeColumn, moveColumn } from 'themes/glitch/actions/columns';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import ColumnSettingsContainer from './containers/column_settings_container';
-import { connectDirectStream } from 'themes/glitch/actions/streaming';
-
-const messages = defineMessages({
-  title: { id: 'column.direct', defaultMessage: 'Direct messages' },
-});
-
-const mapStateToProps = state => ({
-  hasUnread: state.getIn(['timelines', 'direct', 'unread']) > 0,
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class DirectTimeline extends React.PureComponent {
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    columnId: PropTypes.string,
-    intl: PropTypes.object.isRequired,
-    hasUnread: PropTypes.bool,
-    multiColumn: PropTypes.bool,
-  };
-
-  handlePin = () => {
-    const { columnId, dispatch } = this.props;
-
-    if (columnId) {
-      dispatch(removeColumn(columnId));
-    } else {
-      dispatch(addColumn('DIRECT', {}));
-    }
-  }
-
-  handleMove = (dir) => {
-    const { columnId, dispatch } = this.props;
-    dispatch(moveColumn(columnId, dir));
-  }
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  componentDidMount () {
-    const { dispatch } = this.props;
-
-    dispatch(refreshDirectTimeline());
-    this.disconnect = dispatch(connectDirectStream());
-  }
-
-  componentWillUnmount () {
-    if (this.disconnect) {
-      this.disconnect();
-      this.disconnect = null;
-    }
-  }
-
-  setRef = c => {
-    this.column = c;
-  }
-
-  handleLoadMore = () => {
-    this.props.dispatch(expandDirectTimeline());
-  }
-
-  render () {
-    const { intl, hasUnread, columnId, multiColumn } = this.props;
-    const pinned = !!columnId;
-
-    return (
-      <Column ref={this.setRef}>
-        <ColumnHeader
-          icon='envelope'
-          active={hasUnread}
-          title={intl.formatMessage(messages.title)}
-          onPin={this.handlePin}
-          onMove={this.handleMove}
-          onClick={this.handleHeaderClick}
-          pinned={pinned}
-          multiColumn={multiColumn}
-        >
-          <ColumnSettingsContainer />
-        </ColumnHeader>
-
-        <StatusListContainer
-          trackScroll={!pinned}
-          scrollKey={`direct_timeline-${columnId}`}
-          timelineId='direct'
-          loadMore={this.handleLoadMore}
-          emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/favourited_statuses/index.js b/app/javascript/themes/glitch/features/favourited_statuses/index.js
deleted file mode 100644
index 80345e0e2..000000000
--- a/app/javascript/themes/glitch/features/favourited_statuses/index.js
+++ /dev/null
@@ -1,94 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { fetchFavouritedStatuses, expandFavouritedStatuses } from 'themes/glitch/actions/favourites';
-import Column from 'themes/glitch/features/ui/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-import { addColumn, removeColumn, moveColumn } from 'themes/glitch/actions/columns';
-import StatusList from 'themes/glitch/components/status_list';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const messages = defineMessages({
-  heading: { id: 'column.favourites', defaultMessage: 'Favourites' },
-});
-
-const mapStateToProps = state => ({
-  statusIds: state.getIn(['status_lists', 'favourites', 'items']),
-  hasMore: !!state.getIn(['status_lists', 'favourites', 'next']),
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class Favourites extends ImmutablePureComponent {
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    statusIds: ImmutablePropTypes.list.isRequired,
-    intl: PropTypes.object.isRequired,
-    columnId: PropTypes.string,
-    multiColumn: PropTypes.bool,
-    hasMore: PropTypes.bool,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchFavouritedStatuses());
-  }
-
-  handlePin = () => {
-    const { columnId, dispatch } = this.props;
-
-    if (columnId) {
-      dispatch(removeColumn(columnId));
-    } else {
-      dispatch(addColumn('FAVOURITES', {}));
-    }
-  }
-
-  handleMove = (dir) => {
-    const { columnId, dispatch } = this.props;
-    dispatch(moveColumn(columnId, dir));
-  }
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  setRef = c => {
-    this.column = c;
-  }
-
-  handleScrollToBottom = () => {
-    this.props.dispatch(expandFavouritedStatuses());
-  }
-
-  render () {
-    const { intl, statusIds, columnId, multiColumn, hasMore } = this.props;
-    const pinned = !!columnId;
-
-    return (
-      <Column ref={this.setRef} name='favourites'>
-        <ColumnHeader
-          icon='star'
-          title={intl.formatMessage(messages.heading)}
-          onPin={this.handlePin}
-          onMove={this.handleMove}
-          onClick={this.handleHeaderClick}
-          pinned={pinned}
-          multiColumn={multiColumn}
-          showBackButton
-        />
-
-        <StatusList
-          trackScroll={!pinned}
-          statusIds={statusIds}
-          scrollKey={`favourited_statuses-${columnId}`}
-          hasMore={hasMore}
-          onScrollToBottom={this.handleScrollToBottom}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/favourites/index.js b/app/javascript/themes/glitch/features/favourites/index.js
deleted file mode 100644
index d7b8ac3b1..000000000
--- a/app/javascript/themes/glitch/features/favourites/index.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import LoadingIndicator from 'themes/glitch/components/loading_indicator';
-import { fetchFavourites } from 'themes/glitch/actions/interactions';
-import { ScrollContainer } from 'react-router-scroll-4';
-import AccountContainer from 'themes/glitch/containers/account_container';
-import Column from 'themes/glitch/features/ui/components/column';
-import ColumnBackButton from 'themes/glitch/components/column_back_button';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const mapStateToProps = (state, props) => ({
-  accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId]),
-});
-
-@connect(mapStateToProps)
-export default class Favourites extends ImmutablePureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    accountIds: ImmutablePropTypes.list,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchFavourites(this.props.params.statusId));
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
-      this.props.dispatch(fetchFavourites(nextProps.params.statusId));
-    }
-  }
-
-  render () {
-    const { accountIds } = this.props;
-
-    if (!accountIds) {
-      return (
-        <Column>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
-    return (
-      <Column>
-        <ColumnBackButton />
-
-        <ScrollContainer scrollKey='favourites'>
-          <div className='scrollable'>
-            {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)}
-          </div>
-        </ScrollContainer>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/follow_requests/components/account_authorize.js b/app/javascript/themes/glitch/features/follow_requests/components/account_authorize.js
deleted file mode 100644
index ce386d888..000000000
--- a/app/javascript/themes/glitch/features/follow_requests/components/account_authorize.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import Permalink from 'themes/glitch/components/permalink';
-import Avatar from 'themes/glitch/components/avatar';
-import DisplayName from 'themes/glitch/components/display_name';
-import IconButton from 'themes/glitch/components/icon_button';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const messages = defineMessages({
-  authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' },
-  reject: { id: 'follow_request.reject', defaultMessage: 'Reject' },
-});
-
-@injectIntl
-export default class AccountAuthorize extends ImmutablePureComponent {
-
-  static propTypes = {
-    account: ImmutablePropTypes.map.isRequired,
-    onAuthorize: PropTypes.func.isRequired,
-    onReject: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  render () {
-    const { intl, account, onAuthorize, onReject } = this.props;
-    const content = { __html: account.get('note_emojified') };
-
-    return (
-      <div className='account-authorize__wrapper'>
-        <div className='account-authorize'>
-          <Permalink href={account.get('url')} to={`/accounts/${account.get('id')}`} className='detailed-status__display-name'>
-            <div className='account-authorize__avatar'><Avatar account={account} size={48} /></div>
-            <DisplayName account={account} />
-          </Permalink>
-
-          <div className='account__header__content' dangerouslySetInnerHTML={content} />
-        </div>
-
-        <div className='account--panel'>
-          <div className='account--panel__button'><IconButton title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} /></div>
-          <div className='account--panel__button'><IconButton title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} /></div>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/follow_requests/containers/account_authorize_container.js b/app/javascript/themes/glitch/features/follow_requests/containers/account_authorize_container.js
deleted file mode 100644
index 78ae77eee..000000000
--- a/app/javascript/themes/glitch/features/follow_requests/containers/account_authorize_container.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { connect } from 'react-redux';
-import { makeGetAccount } from 'themes/glitch/selectors';
-import AccountAuthorize from '../components/account_authorize';
-import { authorizeFollowRequest, rejectFollowRequest } from 'themes/glitch/actions/accounts';
-
-const makeMapStateToProps = () => {
-  const getAccount = makeGetAccount();
-
-  const mapStateToProps = (state, props) => ({
-    account: getAccount(state, props.id),
-  });
-
-  return mapStateToProps;
-};
-
-const mapDispatchToProps = (dispatch, { id }) => ({
-  onAuthorize () {
-    dispatch(authorizeFollowRequest(id));
-  },
-
-  onReject () {
-    dispatch(rejectFollowRequest(id));
-  },
-});
-
-export default connect(makeMapStateToProps, mapDispatchToProps)(AccountAuthorize);
diff --git a/app/javascript/themes/glitch/features/follow_requests/index.js b/app/javascript/themes/glitch/features/follow_requests/index.js
deleted file mode 100644
index 3f44f518a..000000000
--- a/app/javascript/themes/glitch/features/follow_requests/index.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import LoadingIndicator from 'themes/glitch/components/loading_indicator';
-import { ScrollContainer } from 'react-router-scroll-4';
-import Column from 'themes/glitch/features/ui/components/column';
-import ColumnBackButtonSlim from 'themes/glitch/components/column_back_button_slim';
-import AccountAuthorizeContainer from './containers/account_authorize_container';
-import { fetchFollowRequests, expandFollowRequests } from 'themes/glitch/actions/accounts';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const messages = defineMessages({
-  heading: { id: 'column.follow_requests', defaultMessage: 'Follow requests' },
-});
-
-const mapStateToProps = state => ({
-  accountIds: state.getIn(['user_lists', 'follow_requests', 'items']),
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class FollowRequests extends ImmutablePureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    accountIds: ImmutablePropTypes.list,
-    intl: PropTypes.object.isRequired,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchFollowRequests());
-  }
-
-  handleScroll = (e) => {
-    const { scrollTop, scrollHeight, clientHeight } = e.target;
-
-    if (scrollTop === scrollHeight - clientHeight) {
-      this.props.dispatch(expandFollowRequests());
-    }
-  }
-
-  render () {
-    const { intl, accountIds } = this.props;
-
-    if (!accountIds) {
-      return (
-        <Column name='follow-requests'>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
-    return (
-      <Column name='follow-requests' icon='users' heading={intl.formatMessage(messages.heading)}>
-        <ColumnBackButtonSlim />
-
-        <ScrollContainer scrollKey='follow_requests'>
-          <div className='scrollable' onScroll={this.handleScroll}>
-            {accountIds.map(id =>
-              <AccountAuthorizeContainer key={id} id={id} />
-            )}
-          </div>
-        </ScrollContainer>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/followers/index.js b/app/javascript/themes/glitch/features/followers/index.js
deleted file mode 100644
index d586bf41d..000000000
--- a/app/javascript/themes/glitch/features/followers/index.js
+++ /dev/null
@@ -1,93 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import LoadingIndicator from 'themes/glitch/components/loading_indicator';
-import {
-  fetchAccount,
-  fetchFollowers,
-  expandFollowers,
-} from 'themes/glitch/actions/accounts';
-import { ScrollContainer } from 'react-router-scroll-4';
-import AccountContainer from 'themes/glitch/containers/account_container';
-import Column from 'themes/glitch/features/ui/components/column';
-import HeaderContainer from 'themes/glitch/features/account_timeline/containers/header_container';
-import LoadMore from 'themes/glitch/components/load_more';
-import ColumnBackButton from 'themes/glitch/components/column_back_button';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const mapStateToProps = (state, props) => ({
-  accountIds: state.getIn(['user_lists', 'followers', props.params.accountId, 'items']),
-  hasMore: !!state.getIn(['user_lists', 'followers', props.params.accountId, 'next']),
-});
-
-@connect(mapStateToProps)
-export default class Followers extends ImmutablePureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    accountIds: ImmutablePropTypes.list,
-    hasMore: PropTypes.bool,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchAccount(this.props.params.accountId));
-    this.props.dispatch(fetchFollowers(this.props.params.accountId));
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) {
-      this.props.dispatch(fetchAccount(nextProps.params.accountId));
-      this.props.dispatch(fetchFollowers(nextProps.params.accountId));
-    }
-  }
-
-  handleScroll = (e) => {
-    const { scrollTop, scrollHeight, clientHeight } = e.target;
-
-    if (scrollTop === scrollHeight - clientHeight && this.props.hasMore) {
-      this.props.dispatch(expandFollowers(this.props.params.accountId));
-    }
-  }
-
-  handleLoadMore = (e) => {
-    e.preventDefault();
-    this.props.dispatch(expandFollowers(this.props.params.accountId));
-  }
-
-  render () {
-    const { accountIds, hasMore } = this.props;
-
-    let loadMore = null;
-
-    if (!accountIds) {
-      return (
-        <Column>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
-    if (hasMore) {
-      loadMore = <LoadMore onClick={this.handleLoadMore} />;
-    }
-
-    return (
-      <Column>
-        <ColumnBackButton />
-
-        <ScrollContainer scrollKey='followers'>
-          <div className='scrollable' onScroll={this.handleScroll}>
-            <div className='followers'>
-              <HeaderContainer accountId={this.props.params.accountId} />
-              {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)}
-              {loadMore}
-            </div>
-          </div>
-        </ScrollContainer>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/following/index.js b/app/javascript/themes/glitch/features/following/index.js
deleted file mode 100644
index c306faf21..000000000
--- a/app/javascript/themes/glitch/features/following/index.js
+++ /dev/null
@@ -1,93 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import LoadingIndicator from 'themes/glitch/components/loading_indicator';
-import {
-  fetchAccount,
-  fetchFollowing,
-  expandFollowing,
-} from 'themes/glitch/actions/accounts';
-import { ScrollContainer } from 'react-router-scroll-4';
-import AccountContainer from 'themes/glitch/containers/account_container';
-import Column from 'themes/glitch/features/ui/components/column';
-import HeaderContainer from 'themes/glitch/features/account_timeline/containers/header_container';
-import LoadMore from 'themes/glitch/components/load_more';
-import ColumnBackButton from 'themes/glitch/components/column_back_button';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const mapStateToProps = (state, props) => ({
-  accountIds: state.getIn(['user_lists', 'following', props.params.accountId, 'items']),
-  hasMore: !!state.getIn(['user_lists', 'following', props.params.accountId, 'next']),
-});
-
-@connect(mapStateToProps)
-export default class Following extends ImmutablePureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    accountIds: ImmutablePropTypes.list,
-    hasMore: PropTypes.bool,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchAccount(this.props.params.accountId));
-    this.props.dispatch(fetchFollowing(this.props.params.accountId));
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) {
-      this.props.dispatch(fetchAccount(nextProps.params.accountId));
-      this.props.dispatch(fetchFollowing(nextProps.params.accountId));
-    }
-  }
-
-  handleScroll = (e) => {
-    const { scrollTop, scrollHeight, clientHeight } = e.target;
-
-    if (scrollTop === scrollHeight - clientHeight && this.props.hasMore) {
-      this.props.dispatch(expandFollowing(this.props.params.accountId));
-    }
-  }
-
-  handleLoadMore = (e) => {
-    e.preventDefault();
-    this.props.dispatch(expandFollowing(this.props.params.accountId));
-  }
-
-  render () {
-    const { accountIds, hasMore } = this.props;
-
-    let loadMore = null;
-
-    if (!accountIds) {
-      return (
-        <Column>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
-    if (hasMore) {
-      loadMore = <LoadMore onClick={this.handleLoadMore} />;
-    }
-
-    return (
-      <Column>
-        <ColumnBackButton />
-
-        <ScrollContainer scrollKey='following'>
-          <div className='scrollable' onScroll={this.handleScroll}>
-            <div className='following'>
-              <HeaderContainer accountId={this.props.params.accountId} />
-              {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)}
-              {loadMore}
-            </div>
-          </div>
-        </ScrollContainer>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/generic_not_found/index.js b/app/javascript/themes/glitch/features/generic_not_found/index.js
deleted file mode 100644
index ccd2b87b2..000000000
--- a/app/javascript/themes/glitch/features/generic_not_found/index.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from 'react';
-import Column from 'themes/glitch/features/ui/components/column';
-import MissingIndicator from 'themes/glitch/components/missing_indicator';
-
-const GenericNotFound = () => (
-  <Column>
-    <MissingIndicator />
-  </Column>
-);
-
-export default GenericNotFound;
diff --git a/app/javascript/themes/glitch/features/getting_started/index.js b/app/javascript/themes/glitch/features/getting_started/index.js
deleted file mode 100644
index 74b019cf1..000000000
--- a/app/javascript/themes/glitch/features/getting_started/index.js
+++ /dev/null
@@ -1,145 +0,0 @@
-import React from 'react';
-import Column from 'themes/glitch/features/ui/components/column';
-import ColumnLink from 'themes/glitch/features/ui/components/column_link';
-import ColumnSubheading from 'themes/glitch/features/ui/components/column_subheading';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import { connect } from 'react-redux';
-import { openModal } from 'themes/glitch/actions/modal';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { me } from 'themes/glitch/util/initial_state';
-
-const messages = defineMessages({
-  heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
-  home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' },
-  notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
-  public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' },
-  navigation_subheading: { id: 'column_subheading.navigation', defaultMessage: 'Navigation' },
-  settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings' },
-  community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
-  direct: { id: 'navigation_bar.direct', defaultMessage: 'Direct messages' },
-  preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
-  settings: { id: 'navigation_bar.app_settings', defaultMessage: 'App settings' },
-  follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
-  sign_out: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
-  favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
-  blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
-  mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
-  info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' },
-  show_me_around: { id: 'getting_started.onboarding', defaultMessage: 'Show me around' },
-  pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' },
-});
-
-const mapStateToProps = state => ({
-  myAccount: state.getIn(['accounts', me]),
-  columns: state.getIn(['settings', 'columns']),
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class GettingStarted extends ImmutablePureComponent {
-
-  static propTypes = {
-    intl: PropTypes.object.isRequired,
-    myAccount: ImmutablePropTypes.map.isRequired,
-    columns: ImmutablePropTypes.list,
-    multiColumn: PropTypes.bool,
-    dispatch: PropTypes.func.isRequired,
-  };
-
-  openSettings = () => {
-    this.props.dispatch(openModal('SETTINGS', {}));
-  }
-
-  openOnboardingModal = (e) => {
-    e.preventDefault();
-    this.props.dispatch(openModal('ONBOARDING'));
-  }
-
-  render () {
-    const { intl, myAccount, columns, multiColumn } = this.props;
-
-    let navItems = [];
-
-    if (multiColumn) {
-      if (!columns.find(item => item.get('id') === 'HOME')) {
-        navItems.push(<ColumnLink key='0' icon='home' text={intl.formatMessage(messages.home_timeline)} to='/timelines/home' />);
-      }
-
-      if (!columns.find(item => item.get('id') === 'NOTIFICATIONS')) {
-        navItems.push(<ColumnLink key='1' icon='bell' text={intl.formatMessage(messages.notifications)} to='/notifications' />);
-      }
-
-      if (!columns.find(item => item.get('id') === 'COMMUNITY')) {
-        navItems.push(<ColumnLink key='2' icon='users' text={intl.formatMessage(messages.community_timeline)} to='/timelines/public/local' />);
-      }
-
-      if (!columns.find(item => item.get('id') === 'PUBLIC')) {
-        navItems.push(<ColumnLink key='3' icon='globe' text={intl.formatMessage(messages.public_timeline)} to='/timelines/public' />);
-      }
-    }
-
-    if (!multiColumn || !columns.find(item => item.get('id') === 'DIRECT')) {
-      navItems.push(<ColumnLink key='4' icon='envelope' text={intl.formatMessage(messages.direct)} to='/timelines/direct' />);
-    }
-
-    navItems = navItems.concat([
-      <ColumnLink key='5' icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />,
-      <ColumnLink key='6' icon='thumb-tack' text={intl.formatMessage(messages.pins)} to='/pinned' />,
-    ]);
-
-    if (myAccount.get('locked')) {
-      navItems.push(<ColumnLink key='7' icon='users' text={intl.formatMessage(messages.follow_requests)} to='/follow_requests' />);
-    }
-
-    navItems = navItems.concat([
-      <ColumnLink key='8' icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' />,
-      <ColumnLink key='9' icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' />,
-    ]);
-
-    return (
-      <Column name='getting-started' icon='asterisk' heading={intl.formatMessage(messages.heading)} hideHeadingOnMobile>
-        <div className='scrollable optionally-scrollable'>
-          <div className='getting-started__wrapper'>
-            <ColumnSubheading text={intl.formatMessage(messages.navigation_subheading)} />
-            {navItems}
-            <ColumnSubheading text={intl.formatMessage(messages.settings_subheading)} />
-            <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 icon='cog' text={intl.formatMessage(messages.preferences)} href='/settings/preferences' />
-            <ColumnLink icon='cogs' text={intl.formatMessage(messages.settings)} onClick={this.openSettings} />
-            <ColumnLink icon='sign-out' text={intl.formatMessage(messages.sign_out)} href='/auth/sign_out' method='delete' />
-          </div>
-
-          <div className='getting-started__footer'>
-            <div className='static-content getting-started'>
-              <p>
-                <a href='https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/FAQ.md' rel='noopener' target='_blank'>
-                  <FormattedMessage id='getting_started.faq' defaultMessage='FAQ' />
-                </a>&nbsp;•&nbsp;
-                <a href='https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/User-guide.md' rel='noopener' target='_blank'>
-                  <FormattedMessage id='getting_started.userguide' defaultMessage='User Guide' />
-                </a>&nbsp;•&nbsp;
-                <a href='https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md' rel='noopener' target='_blank'>
-                  <FormattedMessage id='getting_started.appsshort' defaultMessage='Apps' />
-                </a>
-              </p>
-              <p>
-                <FormattedMessage
-                  id='getting_started.open_source_notice'
-                  defaultMessage='Glitchsoc is open source software, a friendly fork of {Mastodon}. You can contribute or report issues on GitHub at {github}.'
-                  values={{
-                    github: <a href='https://github.com/glitch-soc/mastodon' rel='noopener' target='_blank'>glitch-soc/mastodon</a>,
-                    Mastodon: <a href='https://github.com/tootsuite/mastodon' rel='noopener' target='_blank'>Mastodon</a>,
-                  }}
-                />
-              </p>
-            </div>
-          </div>
-        </div>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/hashtag_timeline/index.js b/app/javascript/themes/glitch/features/hashtag_timeline/index.js
deleted file mode 100644
index a878931b3..000000000
--- a/app/javascript/themes/glitch/features/hashtag_timeline/index.js
+++ /dev/null
@@ -1,118 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import StatusListContainer from 'themes/glitch/features/ui/containers/status_list_container';
-import Column from 'themes/glitch/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-import {
-  refreshHashtagTimeline,
-  expandHashtagTimeline,
-} from 'themes/glitch/actions/timelines';
-import { addColumn, removeColumn, moveColumn } from 'themes/glitch/actions/columns';
-import { FormattedMessage } from 'react-intl';
-import { connectHashtagStream } from 'themes/glitch/actions/streaming';
-
-const mapStateToProps = (state, props) => ({
-  hasUnread: state.getIn(['timelines', `hashtag:${props.params.id}`, 'unread']) > 0,
-});
-
-@connect(mapStateToProps)
-export default class HashtagTimeline extends React.PureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    columnId: PropTypes.string,
-    dispatch: PropTypes.func.isRequired,
-    hasUnread: PropTypes.bool,
-    multiColumn: PropTypes.bool,
-  };
-
-  handlePin = () => {
-    const { columnId, dispatch } = this.props;
-
-    if (columnId) {
-      dispatch(removeColumn(columnId));
-    } else {
-      dispatch(addColumn('HASHTAG', { id: this.props.params.id }));
-    }
-  }
-
-  handleMove = (dir) => {
-    const { columnId, dispatch } = this.props;
-    dispatch(moveColumn(columnId, dir));
-  }
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  _subscribe (dispatch, id) {
-    this.disconnect = dispatch(connectHashtagStream(id));
-  }
-
-  _unsubscribe () {
-    if (this.disconnect) {
-      this.disconnect();
-      this.disconnect = null;
-    }
-  }
-
-  componentDidMount () {
-    const { dispatch } = this.props;
-    const { id } = this.props.params;
-
-    dispatch(refreshHashtagTimeline(id));
-    this._subscribe(dispatch, id);
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (nextProps.params.id !== this.props.params.id) {
-      this.props.dispatch(refreshHashtagTimeline(nextProps.params.id));
-      this._unsubscribe();
-      this._subscribe(this.props.dispatch, nextProps.params.id);
-    }
-  }
-
-  componentWillUnmount () {
-    this._unsubscribe();
-  }
-
-  setRef = c => {
-    this.column = c;
-  }
-
-  handleLoadMore = () => {
-    this.props.dispatch(expandHashtagTimeline(this.props.params.id));
-  }
-
-  render () {
-    const { hasUnread, columnId, multiColumn } = this.props;
-    const { id } = this.props.params;
-    const pinned = !!columnId;
-
-    return (
-      <Column ref={this.setRef} name='hashtag'>
-        <ColumnHeader
-          icon='hashtag'
-          active={hasUnread}
-          title={id}
-          onPin={this.handlePin}
-          onMove={this.handleMove}
-          onClick={this.handleHeaderClick}
-          pinned={pinned}
-          multiColumn={multiColumn}
-          showBackButton
-        />
-
-        <StatusListContainer
-          trackScroll={!pinned}
-          scrollKey={`hashtag_timeline-${columnId}`}
-          timelineId={`hashtag:${id}`}
-          loadMore={this.handleLoadMore}
-          emptyMessage={<FormattedMessage id='empty_column.hashtag' defaultMessage='There is nothing in this hashtag yet.' />}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/home_timeline/components/column_settings.js b/app/javascript/themes/glitch/features/home_timeline/components/column_settings.js
deleted file mode 100644
index 533da6c36..000000000
--- a/app/javascript/themes/glitch/features/home_timeline/components/column_settings.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import SettingToggle from 'themes/glitch/features/notifications/components/setting_toggle';
-import SettingText from 'themes/glitch/components/setting_text';
-
-const messages = defineMessages({
-  filter_regex: { id: 'home.column_settings.filter_regex', defaultMessage: 'Filter out by regular expressions' },
-  settings: { id: 'home.settings', defaultMessage: 'Column settings' },
-});
-
-@injectIntl
-export default class ColumnSettings extends React.PureComponent {
-
-  static propTypes = {
-    settings: ImmutablePropTypes.map.isRequired,
-    onChange: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  render () {
-    const { settings, onChange, intl } = this.props;
-
-    return (
-      <div>
-        <span className='column-settings__section'><FormattedMessage id='home.column_settings.basic' defaultMessage='Basic' /></span>
-
-        <div className='column-settings__row'>
-          <SettingToggle prefix='home_timeline' settings={settings} settingKey={['shows', 'reblog']} onChange={onChange} label={<FormattedMessage id='home.column_settings.show_reblogs' defaultMessage='Show boosts' />} />
-        </div>
-
-        <div className='column-settings__row'>
-          <SettingToggle prefix='home_timeline' settings={settings} settingKey={['shows', 'reply']} onChange={onChange} label={<FormattedMessage id='home.column_settings.show_replies' defaultMessage='Show replies' />} />
-        </div>
-
-        <span className='column-settings__section'><FormattedMessage id='home.column_settings.advanced' defaultMessage='Advanced' /></span>
-
-        <div className='column-settings__row'>
-          <SettingText prefix='home_timeline' settings={settings} settingKey={['regex', 'body']} onChange={onChange} label={intl.formatMessage(messages.filter_regex)} />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/home_timeline/containers/column_settings_container.js b/app/javascript/themes/glitch/features/home_timeline/containers/column_settings_container.js
deleted file mode 100644
index a0062f564..000000000
--- a/app/javascript/themes/glitch/features/home_timeline/containers/column_settings_container.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { connect } from 'react-redux';
-import ColumnSettings from '../components/column_settings';
-import { changeSetting, saveSettings } from 'themes/glitch/actions/settings';
-
-const mapStateToProps = state => ({
-  settings: state.getIn(['settings', 'home']),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onChange (key, checked) {
-    dispatch(changeSetting(['home', ...key], checked));
-  },
-
-  onSave () {
-    dispatch(saveSettings());
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);
diff --git a/app/javascript/themes/glitch/features/home_timeline/index.js b/app/javascript/themes/glitch/features/home_timeline/index.js
deleted file mode 100644
index 8a65891cd..000000000
--- a/app/javascript/themes/glitch/features/home_timeline/index.js
+++ /dev/null
@@ -1,90 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import { expandHomeTimeline } from 'themes/glitch/actions/timelines';
-import PropTypes from 'prop-types';
-import StatusListContainer from 'themes/glitch/features/ui/containers/status_list_container';
-import Column from 'themes/glitch/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-import { addColumn, removeColumn, moveColumn } from 'themes/glitch/actions/columns';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import ColumnSettingsContainer from './containers/column_settings_container';
-import { Link } from 'react-router-dom';
-
-const messages = defineMessages({
-  title: { id: 'column.home', defaultMessage: 'Home' },
-});
-
-const mapStateToProps = state => ({
-  hasUnread: state.getIn(['timelines', 'home', 'unread']) > 0,
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class HomeTimeline extends React.PureComponent {
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-    hasUnread: PropTypes.bool,
-    columnId: PropTypes.string,
-    multiColumn: PropTypes.bool,
-  };
-
-  handlePin = () => {
-    const { columnId, dispatch } = this.props;
-
-    if (columnId) {
-      dispatch(removeColumn(columnId));
-    } else {
-      dispatch(addColumn('HOME', {}));
-    }
-  }
-
-  handleMove = (dir) => {
-    const { columnId, dispatch } = this.props;
-    dispatch(moveColumn(columnId, dir));
-  }
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  setRef = c => {
-    this.column = c;
-  }
-
-  handleLoadMore = () => {
-    this.props.dispatch(expandHomeTimeline());
-  }
-
-  render () {
-    const { intl, hasUnread, columnId, multiColumn } = this.props;
-    const pinned = !!columnId;
-
-    return (
-      <Column ref={this.setRef} name='home'>
-        <ColumnHeader
-          icon='home'
-          active={hasUnread}
-          title={intl.formatMessage(messages.title)}
-          onPin={this.handlePin}
-          onMove={this.handleMove}
-          onClick={this.handleHeaderClick}
-          pinned={pinned}
-          multiColumn={multiColumn}
-        >
-          <ColumnSettingsContainer />
-        </ColumnHeader>
-
-        <StatusListContainer
-          trackScroll={!pinned}
-          scrollKey={`home_timeline-${columnId}`}
-          loadMore={this.handleLoadMore}
-          timelineId='home'
-          emptyMessage={<FormattedMessage id='empty_column.home' defaultMessage='Your home timeline is empty! Visit {public} or use search to get started and meet other users.' values={{ public: <Link to='/timelines/public'><FormattedMessage id='empty_column.home.public_timeline' defaultMessage='the public timeline' /></Link> }} />}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/local_settings/index.js b/app/javascript/themes/glitch/features/local_settings/index.js
deleted file mode 100644
index 6c5d51413..000000000
--- a/app/javascript/themes/glitch/features/local_settings/index.js
+++ /dev/null
@@ -1,68 +0,0 @@
-//  Package imports.
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { connect } from 'react-redux';
-
-//  Our imports
-import LocalSettingsPage from './page';
-import LocalSettingsNavigation from './navigation';
-import { closeModal } from 'themes/glitch/actions/modal';
-import { changeLocalSetting } from 'themes/glitch/actions/local_settings';
-
-//  Stylesheet imports
-import './style.scss';
-
-const mapStateToProps = state => ({
-  settings: state.get('local_settings'),
-});
-
-const mapDispatchToProps = dispatch => ({
-  onChange (setting, value) {
-    dispatch(changeLocalSetting(setting, value));
-  },
-  onClose () {
-    dispatch(closeModal());
-  },
-});
-
-class LocalSettings extends React.PureComponent {
-
-  static propTypes = {
-    onChange: PropTypes.func.isRequired,
-    onClose: PropTypes.func.isRequired,
-    settings: ImmutablePropTypes.map.isRequired,
-  };
-
-  state = {
-    currentIndex: 0,
-  };
-
-  navigateTo = (index) =>
-    this.setState({ currentIndex: +index });
-
-  render () {
-
-    const { navigateTo } = this;
-    const { onChange, onClose, settings } = this.props;
-    const { currentIndex } = this.state;
-
-    return (
-      <div className='glitch modal-root__modal local-settings'>
-        <LocalSettingsNavigation
-          index={currentIndex}
-          onClose={onClose}
-          onNavigate={navigateTo}
-        />
-        <LocalSettingsPage
-          index={currentIndex}
-          onChange={onChange}
-          settings={settings}
-        />
-      </div>
-    );
-  }
-
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(LocalSettings);
diff --git a/app/javascript/themes/glitch/features/local_settings/navigation/index.js b/app/javascript/themes/glitch/features/local_settings/navigation/index.js
deleted file mode 100644
index fa35e83c7..000000000
--- a/app/javascript/themes/glitch/features/local_settings/navigation/index.js
+++ /dev/null
@@ -1,74 +0,0 @@
-//  Package imports
-import React from 'react';
-import PropTypes from 'prop-types';
-import { injectIntl, defineMessages } from 'react-intl';
-
-//  Our imports
-import LocalSettingsNavigationItem from './item';
-
-//  Stylesheet imports
-import './style.scss';
-
-//  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-
-const messages = defineMessages({
-  general: {  id: 'settings.general', defaultMessage: 'General' },
-  collapsed: { id: 'settings.collapsed_statuses', defaultMessage: 'Collapsed toots' },
-  media: { id: 'settings.media', defaultMessage: 'Media' },
-  preferences: { id: 'settings.preferences', defaultMessage: 'Preferences' },
-  close: { id: 'settings.close', defaultMessage: 'Close' },
-});
-
-@injectIntl
-export default class LocalSettingsNavigation extends React.PureComponent {
-
-  static propTypes = {
-    index      : PropTypes.number,
-    intl       : PropTypes.object.isRequired,
-    onClose    : PropTypes.func.isRequired,
-    onNavigate : PropTypes.func.isRequired,
-  };
-
-  render () {
-
-    const { index, intl, onClose, onNavigate } = this.props;
-
-    return (
-      <nav className='glitch local-settings__navigation'>
-        <LocalSettingsNavigationItem
-          active={index === 0}
-          index={0}
-          onNavigate={onNavigate}
-          title={intl.formatMessage(messages.general)}
-        />
-        <LocalSettingsNavigationItem
-          active={index === 1}
-          index={1}
-          onNavigate={onNavigate}
-          title={intl.formatMessage(messages.collapsed)}
-        />
-        <LocalSettingsNavigationItem
-          active={index === 2}
-          index={2}
-          onNavigate={onNavigate}
-          title={intl.formatMessage(messages.media)}
-        />
-        <LocalSettingsNavigationItem
-          active={index === 3}
-          href='/settings/preferences'
-          index={3}
-          icon='cog'
-          title={intl.formatMessage(messages.preferences)}
-        />
-        <LocalSettingsNavigationItem
-          active={index === 4}
-          className='close'
-          index={4}
-          onNavigate={onClose}
-          title={intl.formatMessage(messages.close)}
-        />
-      </nav>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/local_settings/navigation/item/index.js b/app/javascript/themes/glitch/features/local_settings/navigation/item/index.js
deleted file mode 100644
index a352d5fb2..000000000
--- a/app/javascript/themes/glitch/features/local_settings/navigation/item/index.js
+++ /dev/null
@@ -1,69 +0,0 @@
-//  Package imports
-import React from 'react';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-
-//  Stylesheet imports
-import './style.scss';
-
-//  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-
-export default class LocalSettingsPage extends React.PureComponent {
-
-  static propTypes = {
-    active: PropTypes.bool,
-    className: PropTypes.string,
-    href: PropTypes.string,
-    icon: PropTypes.string,
-    index: PropTypes.number.isRequired,
-    onNavigate: PropTypes.func,
-    title: PropTypes.string,
-  };
-
-  handleClick = (e) => {
-    const { index, onNavigate } = this.props;
-    if (onNavigate) {
-      onNavigate(index);
-      e.preventDefault();
-    }
-  }
-
-  render () {
-    const { handleClick } = this;
-    const {
-      active,
-      className,
-      href,
-      icon,
-      onNavigate,
-      title,
-    } = this.props;
-
-    const finalClassName = classNames('glitch', 'local-settings__navigation__item', {
-      active,
-    }, className);
-
-    const iconElem = icon ? <i className={`fa fa-fw fa-${icon}`} /> : null;
-
-    if (href) return (
-      <a
-        href={href}
-        className={finalClassName}
-      >
-        {iconElem} {title}
-      </a>
-    );
-    else if (onNavigate) return (
-      <a
-        onClick={handleClick}
-        role='button'
-        tabIndex='0'
-        className={finalClassName}
-      >
-        {iconElem} {title}
-      </a>
-    );
-    else return null;
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/local_settings/navigation/item/style.scss b/app/javascript/themes/glitch/features/local_settings/navigation/item/style.scss
deleted file mode 100644
index 7f7371993..000000000
--- a/app/javascript/themes/glitch/features/local_settings/navigation/item/style.scss
+++ /dev/null
@@ -1,27 +0,0 @@
-@import 'styles/mastodon/variables';
-
-.glitch.local-settings__navigation__item {
-  display: block;
-  padding: 15px 20px;
-  color: inherit;
-  background: $primary-text-color;
-  border-bottom: 1px $ui-primary-color solid;
-  cursor: pointer;
-  text-decoration: none;
-  outline: none;
-  transition: background .3s;
-
-  &:hover {
-    background: $ui-secondary-color;
-  }
-
-  &.active {
-    background: $ui-highlight-color;
-    color: $primary-text-color;
-  }
-
-  &.close, &.close:hover {
-    background: $error-value-color;
-    color: $primary-text-color;
-  }
-}
diff --git a/app/javascript/themes/glitch/features/local_settings/navigation/style.scss b/app/javascript/themes/glitch/features/local_settings/navigation/style.scss
deleted file mode 100644
index 0336f943b..000000000
--- a/app/javascript/themes/glitch/features/local_settings/navigation/style.scss
+++ /dev/null
@@ -1,10 +0,0 @@
-@import 'styles/mastodon/variables';
-
-.glitch.local-settings__navigation {
-  background: $primary-text-color;
-  color: $ui-base-color;
-  width: 200px;
-  font-size: 15px;
-  line-height: 20px;
-  overflow-y: auto;
-}
diff --git a/app/javascript/themes/glitch/features/local_settings/page/index.js b/app/javascript/themes/glitch/features/local_settings/page/index.js
deleted file mode 100644
index 498230f7b..000000000
--- a/app/javascript/themes/glitch/features/local_settings/page/index.js
+++ /dev/null
@@ -1,212 +0,0 @@
-//  Package imports
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
-
-//  Our imports
-import LocalSettingsPageItem from './item';
-
-//  Stylesheet imports
-import './style.scss';
-
-//  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-
-const messages = defineMessages({
-  layout_auto: {  id: 'layout.auto', defaultMessage: 'Auto' },
-  layout_desktop: { id: 'layout.desktop', defaultMessage: 'Desktop' },
-  layout_mobile: { id: 'layout.single', defaultMessage: 'Mobile' },
-  side_arm_none: { id: 'settings.side_arm.none', defaultMessage: 'None' },
-});
-
-@injectIntl
-export default class LocalSettingsPage extends React.PureComponent {
-
-  static propTypes = {
-    index    : PropTypes.number,
-    intl     : PropTypes.object.isRequired,
-    onChange : PropTypes.func.isRequired,
-    settings : ImmutablePropTypes.map.isRequired,
-  };
-
-  pages = [
-    ({ intl, onChange, settings }) => (
-      <div className='glitch local-settings__page general'>
-        <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) },
-          ]}
-          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>
-          <h2><FormattedMessage id='settings.compose_box_opts' defaultMessage='Compose box options' /></h2>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['side_arm']}
-            id='mastodon-settings--side_arm'
-            options={[
-              { value: 'none', message: intl.formatMessage(messages.side_arm_none) },
-              { value: 'direct', message: intl.formatMessage({ id: 'privacy.direct.short' }) },
-              { value: 'private', message: intl.formatMessage({ id: 'privacy.private.short' }) },
-              { value: 'unlisted', message: intl.formatMessage({ id: 'privacy.unlisted.short' }) },
-              { value: 'public', message: intl.formatMessage({ id: 'privacy.public.short' }) },
-            ]}
-            onChange={onChange}
-          >
-            <FormattedMessage id='settings.side_arm' defaultMessage='Secondary toot button:' />
-          </LocalSettingsPageItem>
-        </section>
-      </div>
-    ),
-    ({ onChange, settings }) => (
-      <div className='glitch local-settings__page collapsed'>
-        <h1><FormattedMessage id='settings.collapsed_statuses' defaultMessage='Collapsed toots' /></h1>
-        <LocalSettingsPageItem
-          settings={settings}
-          item={['collapsed', 'enabled']}
-          id='mastodon-settings--collapsed-enabled'
-          onChange={onChange}
-        >
-          <FormattedMessage id='settings.enable_collapsed' defaultMessage='Enable collapsed toots' />
-        </LocalSettingsPageItem>
-        <section>
-          <h2><FormattedMessage id='settings.auto_collapse' defaultMessage='Automatic collapsing' /></h2>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['collapsed', 'auto', 'all']}
-            id='mastodon-settings--collapsed-auto-all'
-            onChange={onChange}
-            dependsOn={[['collapsed', 'enabled']]}
-          >
-            <FormattedMessage id='settings.auto_collapse_all' defaultMessage='Everything' />
-          </LocalSettingsPageItem>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['collapsed', 'auto', 'notifications']}
-            id='mastodon-settings--collapsed-auto-notifications'
-            onChange={onChange}
-            dependsOn={[['collapsed', 'enabled']]}
-            dependsOnNot={[['collapsed', 'auto', 'all']]}
-          >
-            <FormattedMessage id='settings.auto_collapse_notifications' defaultMessage='Notifications' />
-          </LocalSettingsPageItem>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['collapsed', 'auto', 'lengthy']}
-            id='mastodon-settings--collapsed-auto-lengthy'
-            onChange={onChange}
-            dependsOn={[['collapsed', 'enabled']]}
-            dependsOnNot={[['collapsed', 'auto', 'all']]}
-          >
-            <FormattedMessage id='settings.auto_collapse_lengthy' defaultMessage='Lengthy toots' />
-          </LocalSettingsPageItem>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['collapsed', 'auto', 'reblogs']}
-            id='mastodon-settings--collapsed-auto-reblogs'
-            onChange={onChange}
-            dependsOn={[['collapsed', 'enabled']]}
-            dependsOnNot={[['collapsed', 'auto', 'all']]}
-          >
-            <FormattedMessage id='settings.auto_collapse_reblogs' defaultMessage='Boosts' />
-          </LocalSettingsPageItem>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['collapsed', 'auto', 'replies']}
-            id='mastodon-settings--collapsed-auto-replies'
-            onChange={onChange}
-            dependsOn={[['collapsed', 'enabled']]}
-            dependsOnNot={[['collapsed', 'auto', 'all']]}
-          >
-            <FormattedMessage id='settings.auto_collapse_replies' defaultMessage='Replies' />
-          </LocalSettingsPageItem>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['collapsed', 'auto', 'media']}
-            id='mastodon-settings--collapsed-auto-media'
-            onChange={onChange}
-            dependsOn={[['collapsed', 'enabled']]}
-            dependsOnNot={[['collapsed', 'auto', 'all']]}
-          >
-            <FormattedMessage id='settings.auto_collapse_media' defaultMessage='Toots with media' />
-          </LocalSettingsPageItem>
-        </section>
-        <section>
-          <h2><FormattedMessage id='settings.image_backgrounds' defaultMessage='Image backgrounds' /></h2>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['collapsed', 'backgrounds', 'user_backgrounds']}
-            id='mastodon-settings--collapsed-user-backgrouns'
-            onChange={onChange}
-            dependsOn={[['collapsed', 'enabled']]}
-          >
-            <FormattedMessage id='settings.image_backgrounds_users' defaultMessage='Give collapsed toots an image background' />
-          </LocalSettingsPageItem>
-          <LocalSettingsPageItem
-            settings={settings}
-            item={['collapsed', 'backgrounds', 'preview_images']}
-            id='mastodon-settings--collapsed-preview-images'
-            onChange={onChange}
-            dependsOn={[['collapsed', 'enabled']]}
-          >
-            <FormattedMessage id='settings.image_backgrounds_media' defaultMessage='Preview collapsed toot media' />
-          </LocalSettingsPageItem>
-        </section>
-      </div>
-    ),
-    ({ onChange, settings }) => (
-      <div className='glitch local-settings__page media'>
-        <h1><FormattedMessage id='settings.media' defaultMessage='Media' /></h1>
-        <LocalSettingsPageItem
-          settings={settings}
-          item={['media', 'letterbox']}
-          id='mastodon-settings--media-letterbox'
-          onChange={onChange}
-        >
-          <FormattedMessage id='settings.media_letterbox' defaultMessage='Letterbox media' />
-        </LocalSettingsPageItem>
-        <LocalSettingsPageItem
-          settings={settings}
-          item={['media', 'fullwidth']}
-          id='mastodon-settings--media-fullwidth'
-          onChange={onChange}
-        >
-          <FormattedMessage id='settings.media_fullwidth' defaultMessage='Full-width media previews' />
-        </LocalSettingsPageItem>
-      </div>
-    ),
-  ];
-
-  render () {
-    const { pages } = this;
-    const { index, intl, onChange, settings } = this.props;
-    const CurrentPage = pages[index] || pages[0];
-
-    return <CurrentPage intl={intl} onChange={onChange} settings={settings} />;
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/local_settings/page/item/index.js b/app/javascript/themes/glitch/features/local_settings/page/item/index.js
deleted file mode 100644
index 37e28c084..000000000
--- a/app/javascript/themes/glitch/features/local_settings/page/item/index.js
+++ /dev/null
@@ -1,90 +0,0 @@
-//  Package imports
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-
-//  Stylesheet imports
-import './style.scss';
-
-//  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-
-export default class LocalSettingsPageItem extends React.PureComponent {
-
-  static propTypes = {
-    children: PropTypes.element.isRequired,
-    dependsOn: PropTypes.array,
-    dependsOnNot: PropTypes.array,
-    id: PropTypes.string.isRequired,
-    item: PropTypes.array.isRequired,
-    onChange: PropTypes.func.isRequired,
-    options: PropTypes.arrayOf(PropTypes.shape({
-      value: PropTypes.string.isRequired,
-      message: PropTypes.string.isRequired,
-    })),
-    settings: ImmutablePropTypes.map.isRequired,
-  };
-
-  handleChange = e => {
-    const { target } = e;
-    const { item, onChange, options } = this.props;
-    if (options && options.length > 0) onChange(item, target.value);
-    else onChange(item, target.checked);
-  }
-
-  render () {
-    const { handleChange } = this;
-    const { settings, item, id, options, children, dependsOn, dependsOnNot } = this.props;
-    let enabled = true;
-
-    if (dependsOn) {
-      for (let i = 0; i < dependsOn.length; i++) {
-        enabled = enabled && settings.getIn(dependsOn[i]);
-      }
-    }
-    if (dependsOnNot) {
-      for (let i = 0; i < dependsOnNot.length; i++) {
-        enabled = enabled && !settings.getIn(dependsOnNot[i]);
-      }
-    }
-
-    if (options && options.length > 0) {
-      const currentValue = settings.getIn(item);
-      const optionElems = options && options.length > 0 && options.map((opt) => (
-        <option
-          key={opt.value}
-          value={opt.value}
-        >
-          {opt.message}
-        </option>
-      ));
-      return (
-        <label className='glitch local-settings__page__item' htmlFor={id}>
-          <p>{children}</p>
-          <p>
-            <select
-              id={id}
-              disabled={!enabled}
-              onBlur={handleChange}
-              onChange={handleChange}
-              value={currentValue}
-            >
-              {optionElems}
-            </select>
-          </p>
-        </label>
-      );
-    } else return (
-      <label className='glitch local-settings__page__item' htmlFor={id}>
-        <input
-          id={id}
-          type='checkbox'
-          checked={settings.getIn(item)}
-          onChange={handleChange}
-          disabled={!enabled}
-        />
-        {children}
-      </label>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/local_settings/page/item/style.scss b/app/javascript/themes/glitch/features/local_settings/page/item/style.scss
deleted file mode 100644
index b2d8f7185..000000000
--- a/app/javascript/themes/glitch/features/local_settings/page/item/style.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-@import 'styles/mastodon/variables';
-
-.glitch.local-settings__page__item {
-  select {
-    margin-bottom: 5px;
-  }
-}
diff --git a/app/javascript/themes/glitch/features/local_settings/page/style.scss b/app/javascript/themes/glitch/features/local_settings/page/style.scss
deleted file mode 100644
index e9eedcad0..000000000
--- a/app/javascript/themes/glitch/features/local_settings/page/style.scss
+++ /dev/null
@@ -1,9 +0,0 @@
-@import 'styles/mastodon/variables';
-
-.glitch.local-settings__page {
-  display: block;
-  flex: auto;
-  padding: 15px 20px 15px 20px;
-  width: 360px;
-  overflow-y: auto;
-}
diff --git a/app/javascript/themes/glitch/features/local_settings/style.scss b/app/javascript/themes/glitch/features/local_settings/style.scss
deleted file mode 100644
index 765294607..000000000
--- a/app/javascript/themes/glitch/features/local_settings/style.scss
+++ /dev/null
@@ -1,34 +0,0 @@
-@import 'styles/mastodon/variables';
-
-.glitch.local-settings {
-  position: relative;
-  display: flex;
-  flex-direction: row;
-  background: $ui-secondary-color;
-  color: $ui-base-color;
-  border-radius: 8px;
-  height: 80vh;
-  width: 80vw;
-  max-width: 740px;
-  max-height: 450px;
-  overflow: hidden;
-
-  label {
-    display: block;
-  }
-
-  h1 {
-    font-size: 18px;
-    font-weight: 500;
-    line-height: 24px;
-    margin-bottom: 20px;
-  }
-
-  h2 {
-    font-size: 15px;
-    font-weight: 500;
-    line-height: 20px;
-    margin-top: 20px;
-    margin-bottom: 10px;
-  }
-}
diff --git a/app/javascript/themes/glitch/features/mutes/index.js b/app/javascript/themes/glitch/features/mutes/index.js
deleted file mode 100644
index 1158b8262..000000000
--- a/app/javascript/themes/glitch/features/mutes/index.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import LoadingIndicator from 'themes/glitch/components/loading_indicator';
-import { ScrollContainer } from 'react-router-scroll-4';
-import Column from 'themes/glitch/features/ui/components/column';
-import ColumnBackButtonSlim from 'themes/glitch/components/column_back_button_slim';
-import AccountContainer from 'themes/glitch/containers/account_container';
-import { fetchMutes, expandMutes } from 'themes/glitch/actions/mutes';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const messages = defineMessages({
-  heading: { id: 'column.mutes', defaultMessage: 'Muted users' },
-});
-
-const mapStateToProps = state => ({
-  accountIds: state.getIn(['user_lists', 'mutes', 'items']),
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class Mutes extends ImmutablePureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    accountIds: ImmutablePropTypes.list,
-    intl: PropTypes.object.isRequired,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchMutes());
-  }
-
-  handleScroll = (e) => {
-    const { scrollTop, scrollHeight, clientHeight } = e.target;
-
-    if (scrollTop === scrollHeight - clientHeight) {
-      this.props.dispatch(expandMutes());
-    }
-  }
-
-  render () {
-    const { intl, accountIds } = this.props;
-
-    if (!accountIds) {
-      return (
-        <Column>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
-    return (
-      <Column name='mutes' icon='volume-off' heading={intl.formatMessage(messages.heading)}>
-        <ColumnBackButtonSlim />
-        <ScrollContainer scrollKey='mutes'>
-          <div className='scrollable mutes' onScroll={this.handleScroll}>
-            {accountIds.map(id =>
-              <AccountContainer key={id} id={id} />
-            )}
-          </div>
-        </ScrollContainer>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/notifications/components/clear_column_button.js b/app/javascript/themes/glitch/features/notifications/components/clear_column_button.js
deleted file mode 100644
index 22a10753f..000000000
--- a/app/javascript/themes/glitch/features/notifications/components/clear_column_button.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { FormattedMessage } from 'react-intl';
-
-export default class ClearColumnButton extends React.Component {
-
-  static propTypes = {
-    onClick: PropTypes.func.isRequired,
-  };
-
-  render () {
-    return (
-      <button className='text-btn column-header__setting-btn' tabIndex='0' onClick={this.props.onClick}><i className='fa fa-eraser' /> <FormattedMessage id='notifications.clear' defaultMessage='Clear notifications' /></button>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/notifications/components/column_settings.js b/app/javascript/themes/glitch/features/notifications/components/column_settings.js
deleted file mode 100644
index 88a29d4d3..000000000
--- a/app/javascript/themes/glitch/features/notifications/components/column_settings.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { FormattedMessage } from 'react-intl';
-import ClearColumnButton from './clear_column_button';
-import SettingToggle from './setting_toggle';
-
-export default class ColumnSettings extends React.PureComponent {
-
-  static propTypes = {
-    settings: ImmutablePropTypes.map.isRequired,
-    pushSettings: ImmutablePropTypes.map.isRequired,
-    onChange: PropTypes.func.isRequired,
-    onSave: PropTypes.func.isRequired,
-    onClear: PropTypes.func.isRequired,
-  };
-
-  onPushChange = (key, checked) => {
-    this.props.onChange(['push', ...key], checked);
-  }
-
-  render () {
-    const { settings, pushSettings, onChange, onClear } = this.props;
-
-    const alertStr = <FormattedMessage id='notifications.column_settings.alert' defaultMessage='Desktop notifications' />;
-    const showStr  = <FormattedMessage id='notifications.column_settings.show' defaultMessage='Show in column' />;
-    const soundStr = <FormattedMessage id='notifications.column_settings.sound' defaultMessage='Play sound' />;
-
-    const showPushSettings = pushSettings.get('browserSupport') && pushSettings.get('isSubscribed');
-    const pushStr = showPushSettings && <FormattedMessage id='notifications.column_settings.push' defaultMessage='Push notifications' />;
-    const pushMeta = showPushSettings && <FormattedMessage id='notifications.column_settings.push_meta' defaultMessage='This device' />;
-
-    return (
-      <div>
-        <div className='column-settings__row'>
-          <ClearColumnButton onClick={onClear} />
-        </div>
-
-        <div role='group' aria-labelledby='notifications-follow'>
-          <span id='notifications-follow' className='column-settings__section'><FormattedMessage id='notifications.column_settings.follow' defaultMessage='New followers:' /></span>
-
-          <div className='column-settings__row'>
-            <SettingToggle prefix='notifications_desktop' settings={settings} settingKey={['alerts', 'follow']} onChange={onChange} label={alertStr} />
-            {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingKey={['alerts', 'follow']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
-            <SettingToggle prefix='notifications' settings={settings} settingKey={['shows', 'follow']} onChange={onChange} label={showStr} />
-            <SettingToggle prefix='notifications' settings={settings} settingKey={['sounds', 'follow']} onChange={onChange} label={soundStr} />
-          </div>
-        </div>
-
-        <div role='group' aria-labelledby='notifications-favourite'>
-          <span id='notifications-favourite' className='column-settings__section'><FormattedMessage id='notifications.column_settings.favourite' defaultMessage='Favourites:' /></span>
-
-          <div className='column-settings__row'>
-            <SettingToggle prefix='notifications_desktop' settings={settings} settingKey={['alerts', 'favourite']} onChange={onChange} label={alertStr} />
-            {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingKey={['alerts', 'favourite']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
-            <SettingToggle prefix='notifications' settings={settings} settingKey={['shows', 'favourite']} onChange={onChange} label={showStr} />
-            <SettingToggle prefix='notifications' settings={settings} settingKey={['sounds', 'favourite']} onChange={onChange} label={soundStr} />
-          </div>
-        </div>
-
-        <div role='group' aria-labelledby='notifications-mention'>
-          <span id='notifications-mention' className='column-settings__section'><FormattedMessage id='notifications.column_settings.mention' defaultMessage='Mentions:' /></span>
-
-          <div className='column-settings__row'>
-            <SettingToggle prefix='notifications_desktop' settings={settings} settingKey={['alerts', 'mention']} onChange={onChange} label={alertStr} />
-            {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingKey={['alerts', 'mention']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
-            <SettingToggle prefix='notifications' settings={settings} settingKey={['shows', 'mention']} onChange={onChange} label={showStr} />
-            <SettingToggle prefix='notifications' settings={settings} settingKey={['sounds', 'mention']} onChange={onChange} label={soundStr} />
-          </div>
-        </div>
-
-        <div role='group' aria-labelledby='notifications-reblog'>
-          <span id='notifications-reblog' className='column-settings__section'><FormattedMessage id='notifications.column_settings.reblog' defaultMessage='Boosts:' /></span>
-
-          <div className='column-settings__row'>
-            <SettingToggle prefix='notifications_desktop' settings={settings} settingKey={['alerts', 'reblog']} onChange={onChange} label={alertStr} />
-            {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingKey={['alerts', 'reblog']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
-            <SettingToggle prefix='notifications' settings={settings} settingKey={['shows', 'reblog']} onChange={onChange} label={showStr} />
-            <SettingToggle prefix='notifications' settings={settings} settingKey={['sounds', 'reblog']} onChange={onChange} label={soundStr} />
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/notifications/components/follow.js b/app/javascript/themes/glitch/features/notifications/components/follow.js
deleted file mode 100644
index 96cfe83e6..000000000
--- a/app/javascript/themes/glitch/features/notifications/components/follow.js
+++ /dev/null
@@ -1,98 +0,0 @@
-//  Package imports.
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import { FormattedMessage } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { HotKeys } from 'react-hotkeys';
-
-// Our imports.
-import Permalink from 'themes/glitch/components/permalink';
-import AccountContainer from 'themes/glitch/containers/account_container';
-import NotificationOverlayContainer from '../containers/overlay_container';
-
-export default class NotificationFollow extends ImmutablePureComponent {
-
-  static propTypes = {
-    hidden: PropTypes.bool,
-    id: PropTypes.string.isRequired,
-    account: ImmutablePropTypes.map.isRequired,
-    notification: ImmutablePropTypes.map.isRequired,
-  };
-
-  handleMoveUp = () => {
-    const { notification, onMoveUp } = this.props;
-    onMoveUp(notification.get('id'));
-  }
-
-  handleMoveDown = () => {
-    const { notification, onMoveDown } = this.props;
-    onMoveDown(notification.get('id'));
-  }
-
-  handleOpen = () => {
-    this.handleOpenProfile();
-  }
-
-  handleOpenProfile = () => {
-    const { notification } = this.props;
-    this.context.router.history.push(`/accounts/${notification.getIn(['account', 'id'])}`);
-  }
-
-  handleMention = e => {
-    e.preventDefault();
-
-    const { notification, onMention } = this.props;
-    onMention(notification.get('account'), this.context.router.history);
-  }
-
-  getHandlers () {
-    return {
-      moveUp: this.handleMoveUp,
-      moveDown: this.handleMoveDown,
-      open: this.handleOpen,
-      openProfile: this.handleOpenProfile,
-      mention: this.handleMention,
-      reply: this.handleMention,
-    };
-  }
-
-  render () {
-    const { account, notification, hidden } = this.props;
-
-    //  Links to the display name.
-    const displayName = account.get('display_name_html') || account.get('username');
-    const link = (
-      <Permalink
-        className='notification__display-name'
-        href={account.get('url')}
-        title={account.get('acct')}
-        to={`/accounts/${account.get('id')}`}
-        dangerouslySetInnerHTML={{ __html: displayName }}
-      />
-    );
-
-    //  Renders.
-    return (
-      <HotKeys handlers={this.getHandlers()}>
-        <div className='notification notification-follow focusable' tabIndex='0'>
-          <div className='notification__message'>
-            <div className='notification__favourite-icon-wrapper'>
-              <i className='fa fa-fw fa-user-plus' />
-            </div>
-
-            <FormattedMessage
-              id='notification.follow'
-              defaultMessage='{name} followed you'
-              values={{ name: link }}
-            />
-          </div>
-
-          <AccountContainer hidden={hidden} id={account.get('id')} withNote={false} />
-          <NotificationOverlayContainer notification={notification} />
-        </div>
-      </HotKeys>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/notifications/components/notification.js b/app/javascript/themes/glitch/features/notifications/components/notification.js
deleted file mode 100644
index 47f5770bf..000000000
--- a/app/javascript/themes/glitch/features/notifications/components/notification.js
+++ /dev/null
@@ -1,93 +0,0 @@
-//  Package imports.
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-//  Our imports,
-import StatusContainer from 'themes/glitch/containers/status_container';
-import NotificationFollow from './follow';
-
-export default class Notification extends ImmutablePureComponent {
-
-  static propTypes = {
-    notification: ImmutablePropTypes.map.isRequired,
-    hidden: PropTypes.bool,
-    onMoveUp: PropTypes.func.isRequired,
-    onMoveDown: PropTypes.func.isRequired,
-    onMention: PropTypes.func.isRequired,
-  };
-
-  render () {
-    const {
-      hidden,
-      notification,
-      onMoveDown,
-      onMoveUp,
-      onMention,
-    } = this.props;
-
-    switch(notification.get('type')) {
-    case 'follow':
-      return (
-        <NotificationFollow
-          hidden={hidden}
-          id={notification.get('id')}
-          account={notification.get('account')}
-          notification={notification}
-          onMoveDown={onMoveDown}
-          onMoveUp={onMoveUp}
-          onMention={onMention}
-        />
-      );
-    case 'mention':
-      return (
-        <StatusContainer
-          containerId={notification.get('id')}
-          hidden={hidden}
-          id={notification.get('status')}
-          notification={notification}
-          onMoveDown={onMoveDown}
-          onMoveUp={onMoveUp}
-          onMention={onMention}
-          withDismiss
-        />
-      );
-    case 'favourite':
-      return (
-        <StatusContainer
-          containerId={notification.get('id')}
-          hidden={hidden}
-          id={notification.get('status')}
-          account={notification.get('account')}
-          prepend='favourite'
-          muted
-          notification={notification}
-          onMoveDown={onMoveDown}
-          onMoveUp={onMoveUp}
-          onMention={onMention}
-          withDismiss
-        />
-      );
-    case 'reblog':
-      return (
-        <StatusContainer
-          containerId={notification.get('id')}
-          hidden={hidden}
-          id={notification.get('status')}
-          account={notification.get('account')}
-          prepend='reblog'
-          muted
-          notification={notification}
-          onMoveDown={onMoveDown}
-          onMoveUp={onMoveUp}
-          onMention={onMention}
-          withDismiss
-        />
-      );
-    default:
-      return null;
-    }
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/notifications/components/overlay.js b/app/javascript/themes/glitch/features/notifications/components/overlay.js
deleted file mode 100644
index e56f9c628..000000000
--- a/app/javascript/themes/glitch/features/notifications/components/overlay.js
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * Notification overlay
- */
-
-
-//  Package imports.
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { defineMessages, injectIntl } from 'react-intl';
-
-const messages = defineMessages({
-  markForDeletion: { id: 'notification.markForDeletion', defaultMessage: 'Mark for deletion' },
-});
-
-@injectIntl
-export default class NotificationOverlay extends ImmutablePureComponent {
-
-  static propTypes = {
-    notification    : ImmutablePropTypes.map.isRequired,
-    onMarkForDelete : PropTypes.func.isRequired,
-    show            : PropTypes.bool.isRequired,
-    intl            : PropTypes.object.isRequired,
-  };
-
-  onToggleMark = () => {
-    const mark = !this.props.notification.get('markedForDelete');
-    const id = this.props.notification.get('id');
-    this.props.onMarkForDelete(id, mark);
-  }
-
-  render () {
-    const { notification, show, intl } = this.props;
-
-    const active = notification.get('markedForDelete');
-    const label = intl.formatMessage(messages.markForDeletion);
-
-    return show ? (
-      <div
-        aria-label={label}
-        role='checkbox'
-        aria-checked={active}
-        tabIndex={0}
-        className={`notification__dismiss-overlay ${active ? 'active' : ''}`}
-        onClick={this.onToggleMark}
-      >
-        <div className='wrappy'>
-          <div className='ckbox' aria-hidden='true' title={label}>
-            {active ? (<i className='fa fa-check' />) : ''}
-          </div>
-        </div>
-      </div>
-    ) : null;
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/notifications/components/setting_toggle.js b/app/javascript/themes/glitch/features/notifications/components/setting_toggle.js
deleted file mode 100644
index 281359d2a..000000000
--- a/app/javascript/themes/glitch/features/notifications/components/setting_toggle.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import Toggle from 'react-toggle';
-
-export default class SettingToggle extends React.PureComponent {
-
-  static propTypes = {
-    prefix: PropTypes.string,
-    settings: ImmutablePropTypes.map.isRequired,
-    settingKey: PropTypes.array.isRequired,
-    label: PropTypes.node.isRequired,
-    meta: PropTypes.node,
-    onChange: PropTypes.func.isRequired,
-  }
-
-  onChange = ({ target }) => {
-    this.props.onChange(this.props.settingKey, target.checked);
-  }
-
-  render () {
-    const { prefix, settings, settingKey, label, meta } = this.props;
-    const id = ['setting-toggle', prefix, ...settingKey].filter(Boolean).join('-');
-
-    return (
-      <div className='setting-toggle'>
-        <Toggle id={id} checked={settings.getIn(settingKey)} onChange={this.onChange} onKeyDown={this.onKeyDown} />
-        <label htmlFor={id} className='setting-toggle__label'>{label}</label>
-        {meta && <span className='setting-meta__label'>{meta}</span>}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/notifications/containers/column_settings_container.js b/app/javascript/themes/glitch/features/notifications/containers/column_settings_container.js
deleted file mode 100644
index ddc8495f4..000000000
--- a/app/javascript/themes/glitch/features/notifications/containers/column_settings_container.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import { connect } from 'react-redux';
-import { defineMessages, injectIntl } from 'react-intl';
-import ColumnSettings from '../components/column_settings';
-import { changeSetting, saveSettings } from 'themes/glitch/actions/settings';
-import { clearNotifications } from 'themes/glitch/actions/notifications';
-import { changeAlerts as changePushNotifications, saveSettings as savePushNotificationSettings } from 'themes/glitch/actions/push_notifications';
-import { openModal } from 'themes/glitch/actions/modal';
-
-const messages = defineMessages({
-  clearMessage: { id: 'notifications.clear_confirmation', defaultMessage: 'Are you sure you want to permanently clear all your notifications?' },
-  clearConfirm: { id: 'notifications.clear', defaultMessage: 'Clear notifications' },
-});
-
-const mapStateToProps = state => ({
-  settings: state.getIn(['settings', 'notifications']),
-  pushSettings: state.get('push_notifications'),
-});
-
-const mapDispatchToProps = (dispatch, { intl }) => ({
-
-  onChange (key, checked) {
-    if (key[0] === 'push') {
-      dispatch(changePushNotifications(key.slice(1), checked));
-    } else {
-      dispatch(changeSetting(['notifications', ...key], checked));
-    }
-  },
-
-  onSave () {
-    dispatch(saveSettings());
-    dispatch(savePushNotificationSettings());
-  },
-
-  onClear () {
-    dispatch(openModal('CONFIRM', {
-      message: intl.formatMessage(messages.clearMessage),
-      confirm: intl.formatMessage(messages.clearConfirm),
-      onConfirm: () => dispatch(clearNotifications()),
-    }));
-  },
-
-});
-
-export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ColumnSettings));
diff --git a/app/javascript/themes/glitch/features/notifications/containers/notification_container.js b/app/javascript/themes/glitch/features/notifications/containers/notification_container.js
deleted file mode 100644
index 033c61e3d..000000000
--- a/app/javascript/themes/glitch/features/notifications/containers/notification_container.js
+++ /dev/null
@@ -1,26 +0,0 @@
-//  Package imports.
-import { connect } from 'react-redux';
-
-//  Our imports.
-import { makeGetNotification } from 'themes/glitch/selectors';
-import Notification from '../components/notification';
-import { mentionCompose } from 'themes/glitch/actions/compose';
-
-const makeMapStateToProps = () => {
-  const getNotification = makeGetNotification();
-
-  const mapStateToProps = (state, props) => ({
-    notification: getNotification(state, props.notification, props.accountId),
-    notifCleaning: state.getIn(['notifications', 'cleaningMode']),
-  });
-
-  return mapStateToProps;
-};
-
-const mapDispatchToProps = dispatch => ({
-  onMention: (account, router) => {
-    dispatch(mentionCompose(account, router));
-  },
-});
-
-export default connect(makeMapStateToProps, mapDispatchToProps)(Notification);
diff --git a/app/javascript/themes/glitch/features/notifications/containers/overlay_container.js b/app/javascript/themes/glitch/features/notifications/containers/overlay_container.js
deleted file mode 100644
index 52649cdd7..000000000
--- a/app/javascript/themes/glitch/features/notifications/containers/overlay_container.js
+++ /dev/null
@@ -1,18 +0,0 @@
-//  Package imports.
-import { connect } from 'react-redux';
-
-//  Our imports.
-import NotificationOverlay from '../components/overlay';
-import { markNotificationForDelete } from 'themes/glitch/actions/notifications';
-
-const mapDispatchToProps = dispatch => ({
-  onMarkForDelete(id, yes) {
-    dispatch(markNotificationForDelete(id, yes));
-  },
-});
-
-const mapStateToProps = state => ({
-  show: state.getIn(['notifications', 'cleaningMode']),
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(NotificationOverlay);
diff --git a/app/javascript/themes/glitch/features/notifications/index.js b/app/javascript/themes/glitch/features/notifications/index.js
deleted file mode 100644
index 1ecde660a..000000000
--- a/app/javascript/themes/glitch/features/notifications/index.js
+++ /dev/null
@@ -1,193 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import Column from 'themes/glitch/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-import {
-  enterNotificationClearingMode,
-  expandNotifications,
-  scrollTopNotifications,
-} from 'themes/glitch/actions/notifications';
-import { addColumn, removeColumn, moveColumn } from 'themes/glitch/actions/columns';
-import NotificationContainer from './containers/notification_container';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import ColumnSettingsContainer from './containers/column_settings_container';
-import { createSelector } from 'reselect';
-import { List as ImmutableList } from 'immutable';
-import { debounce } from 'lodash';
-import ScrollableList from 'themes/glitch/components/scrollable_list';
-
-const messages = defineMessages({
-  title: { id: 'column.notifications', defaultMessage: 'Notifications' },
-});
-
-const getNotifications = createSelector([
-  state => ImmutableList(state.getIn(['settings', 'notifications', 'shows']).filter(item => !item).keys()),
-  state => state.getIn(['notifications', 'items']),
-], (excludedTypes, notifications) => notifications.filterNot(item => excludedTypes.includes(item.get('type'))));
-
-const mapStateToProps = state => ({
-  notifications: getNotifications(state),
-  localSettings:  state.get('local_settings'),
-  isLoading: state.getIn(['notifications', 'isLoading'], true),
-  isUnread: state.getIn(['notifications', 'unread']) > 0,
-  hasMore: !!state.getIn(['notifications', 'next']),
-  notifCleaningActive: state.getIn(['notifications', 'cleaningMode']),
-});
-
-/* glitch */
-const mapDispatchToProps = dispatch => ({
-  onEnterCleaningMode(yes) {
-    dispatch(enterNotificationClearingMode(yes));
-  },
-  dispatch,
-});
-
-@connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
-export default class Notifications extends React.PureComponent {
-
-  static propTypes = {
-    columnId: PropTypes.string,
-    notifications: ImmutablePropTypes.list.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    shouldUpdateScroll: PropTypes.func,
-    intl: PropTypes.object.isRequired,
-    isLoading: PropTypes.bool,
-    isUnread: PropTypes.bool,
-    multiColumn: PropTypes.bool,
-    hasMore: PropTypes.bool,
-    localSettings: ImmutablePropTypes.map,
-    notifCleaningActive: PropTypes.bool,
-    onEnterCleaningMode: PropTypes.func,
-  };
-
-  static defaultProps = {
-    trackScroll: true,
-  };
-
-  handleScrollToBottom = debounce(() => {
-    this.props.dispatch(scrollTopNotifications(false));
-    this.props.dispatch(expandNotifications());
-  }, 300, { leading: true });
-
-  handleScrollToTop = debounce(() => {
-    this.props.dispatch(scrollTopNotifications(true));
-  }, 100);
-
-  handleScroll = debounce(() => {
-    this.props.dispatch(scrollTopNotifications(false));
-  }, 100);
-
-  handlePin = () => {
-    const { columnId, dispatch } = this.props;
-
-    if (columnId) {
-      dispatch(removeColumn(columnId));
-    } else {
-      dispatch(addColumn('NOTIFICATIONS', {}));
-    }
-  }
-
-  handleMove = (dir) => {
-    const { columnId, dispatch } = this.props;
-    dispatch(moveColumn(columnId, dir));
-  }
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  setColumnRef = c => {
-    this.column = c;
-  }
-
-  handleMoveUp = id => {
-    const elementIndex = this.props.notifications.findIndex(item => item.get('id') === id) - 1;
-    this._selectChild(elementIndex);
-  }
-
-  handleMoveDown = id => {
-    const elementIndex = this.props.notifications.findIndex(item => item.get('id') === id) + 1;
-    this._selectChild(elementIndex);
-  }
-
-  _selectChild (index) {
-    const element = this.column.node.querySelector(`article:nth-of-type(${index + 1}) .focusable`);
-
-    if (element) {
-      element.focus();
-    }
-  }
-
-  render () {
-    const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore } = this.props;
-    const pinned = !!columnId;
-    const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. Interact with others to start the conversation." />;
-
-    let scrollableContent = null;
-
-    if (isLoading && this.scrollableContent) {
-      scrollableContent = this.scrollableContent;
-    } else if (notifications.size > 0 || hasMore) {
-      scrollableContent = notifications.map((item) => (
-        <NotificationContainer
-          key={item.get('id')}
-          notification={item}
-          accountId={item.get('account')}
-          onMoveUp={this.handleMoveUp}
-          onMoveDown={this.handleMoveDown}
-        />
-      ));
-    } else {
-      scrollableContent = null;
-    }
-
-    this.scrollableContent = scrollableContent;
-
-    const scrollContainer = (
-      <ScrollableList
-        scrollKey={`notifications-${columnId}`}
-        trackScroll={!pinned}
-        isLoading={isLoading}
-        hasMore={hasMore}
-        emptyMessage={emptyMessage}
-        onScrollToBottom={this.handleScrollToBottom}
-        onScrollToTop={this.handleScrollToTop}
-        onScroll={this.handleScroll}
-        shouldUpdateScroll={shouldUpdateScroll}
-      >
-        {scrollableContent}
-      </ScrollableList>
-    );
-
-    return (
-      <Column
-        ref={this.setColumnRef}
-        name='notifications'
-        extraClasses={this.props.notifCleaningActive ? 'notif-cleaning' : null}
-      >
-        <ColumnHeader
-          icon='bell'
-          active={isUnread}
-          title={intl.formatMessage(messages.title)}
-          onPin={this.handlePin}
-          onMove={this.handleMove}
-          onClick={this.handleHeaderClick}
-          pinned={pinned}
-          multiColumn={multiColumn}
-          localSettings={this.props.localSettings}
-          notifCleaning
-          notifCleaningActive={this.props.notifCleaningActive} // this is used to toggle the header text
-          onEnterCleaningMode={this.props.onEnterCleaningMode}
-        >
-          <ColumnSettingsContainer />
-        </ColumnHeader>
-
-        {scrollContainer}
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/pinned_statuses/index.js b/app/javascript/themes/glitch/features/pinned_statuses/index.js
deleted file mode 100644
index 0a3997850..000000000
--- a/app/javascript/themes/glitch/features/pinned_statuses/index.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { fetchPinnedStatuses } from 'themes/glitch/actions/pin_statuses';
-import Column from 'themes/glitch/features/ui/components/column';
-import ColumnBackButtonSlim from 'themes/glitch/components/column_back_button_slim';
-import StatusList from 'themes/glitch/components/status_list';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const messages = defineMessages({
-  heading: { id: 'column.pins', defaultMessage: 'Pinned toot' },
-});
-
-const mapStateToProps = state => ({
-  statusIds: state.getIn(['status_lists', 'pins', 'items']),
-  hasMore: !!state.getIn(['status_lists', 'pins', 'next']),
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class PinnedStatuses extends ImmutablePureComponent {
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    statusIds: ImmutablePropTypes.list.isRequired,
-    intl: PropTypes.object.isRequired,
-    hasMore: PropTypes.bool.isRequired,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchPinnedStatuses());
-  }
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  setRef = c => {
-    this.column = c;
-  }
-
-  render () {
-    const { intl, statusIds, hasMore } = this.props;
-
-    return (
-      <Column icon='thumb-tack' heading={intl.formatMessage(messages.heading)} ref={this.setRef}>
-        <ColumnBackButtonSlim />
-        <StatusList
-          statusIds={statusIds}
-          scrollKey='pinned_statuses'
-          hasMore={hasMore}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/public_timeline/containers/column_settings_container.js b/app/javascript/themes/glitch/features/public_timeline/containers/column_settings_container.js
deleted file mode 100644
index 0185a7724..000000000
--- a/app/javascript/themes/glitch/features/public_timeline/containers/column_settings_container.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { connect } from 'react-redux';
-import ColumnSettings from 'themes/glitch/features/community_timeline/components/column_settings';
-import { changeSetting } from 'themes/glitch/actions/settings';
-
-const mapStateToProps = state => ({
-  settings: state.getIn(['settings', 'public']),
-});
-
-const mapDispatchToProps = dispatch => ({
-
-  onChange (key, checked) {
-    dispatch(changeSetting(['public', ...key], checked));
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);
diff --git a/app/javascript/themes/glitch/features/public_timeline/index.js b/app/javascript/themes/glitch/features/public_timeline/index.js
deleted file mode 100644
index f5b3865af..000000000
--- a/app/javascript/themes/glitch/features/public_timeline/index.js
+++ /dev/null
@@ -1,107 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import StatusListContainer from 'themes/glitch/features/ui/containers/status_list_container';
-import Column from 'themes/glitch/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-import {
-  refreshPublicTimeline,
-  expandPublicTimeline,
-} from 'themes/glitch/actions/timelines';
-import { addColumn, removeColumn, moveColumn } from 'themes/glitch/actions/columns';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import ColumnSettingsContainer from './containers/column_settings_container';
-import { connectPublicStream } from 'themes/glitch/actions/streaming';
-
-const messages = defineMessages({
-  title: { id: 'column.public', defaultMessage: 'Federated timeline' },
-});
-
-const mapStateToProps = state => ({
-  hasUnread: state.getIn(['timelines', 'public', 'unread']) > 0,
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class PublicTimeline extends React.PureComponent {
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-    columnId: PropTypes.string,
-    multiColumn: PropTypes.bool,
-    hasUnread: PropTypes.bool,
-  };
-
-  handlePin = () => {
-    const { columnId, dispatch } = this.props;
-
-    if (columnId) {
-      dispatch(removeColumn(columnId));
-    } else {
-      dispatch(addColumn('PUBLIC', {}));
-    }
-  }
-
-  handleMove = (dir) => {
-    const { columnId, dispatch } = this.props;
-    dispatch(moveColumn(columnId, dir));
-  }
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  componentDidMount () {
-    const { dispatch } = this.props;
-
-    dispatch(refreshPublicTimeline());
-    this.disconnect = dispatch(connectPublicStream());
-  }
-
-  componentWillUnmount () {
-    if (this.disconnect) {
-      this.disconnect();
-      this.disconnect = null;
-    }
-  }
-
-  setRef = c => {
-    this.column = c;
-  }
-
-  handleLoadMore = () => {
-    this.props.dispatch(expandPublicTimeline());
-  }
-
-  render () {
-    const { intl, columnId, hasUnread, multiColumn } = this.props;
-    const pinned = !!columnId;
-
-    return (
-      <Column ref={this.setRef} name='federated'>
-        <ColumnHeader
-          icon='globe'
-          active={hasUnread}
-          title={intl.formatMessage(messages.title)}
-          onPin={this.handlePin}
-          onMove={this.handleMove}
-          onClick={this.handleHeaderClick}
-          pinned={pinned}
-          multiColumn={multiColumn}
-        >
-          <ColumnSettingsContainer />
-        </ColumnHeader>
-
-        <StatusListContainer
-          timelineId='public'
-          loadMore={this.handleLoadMore}
-          trackScroll={!pinned}
-          scrollKey={`public_timeline-${columnId}`}
-          emptyMessage={<FormattedMessage id='empty_column.public' defaultMessage='There is nothing here! Write something publicly, or manually follow users from other instances to fill it up' />}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/reblogs/index.js b/app/javascript/themes/glitch/features/reblogs/index.js
deleted file mode 100644
index 8723f7c7c..000000000
--- a/app/javascript/themes/glitch/features/reblogs/index.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import LoadingIndicator from 'themes/glitch/components/loading_indicator';
-import { fetchReblogs } from 'themes/glitch/actions/interactions';
-import { ScrollContainer } from 'react-router-scroll-4';
-import AccountContainer from 'themes/glitch/containers/account_container';
-import Column from 'themes/glitch/features/ui/components/column';
-import ColumnBackButton from 'themes/glitch/components/column_back_button';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const mapStateToProps = (state, props) => ({
-  accountIds: state.getIn(['user_lists', 'reblogged_by', props.params.statusId]),
-});
-
-@connect(mapStateToProps)
-export default class Reblogs extends ImmutablePureComponent {
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    accountIds: ImmutablePropTypes.list,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchReblogs(this.props.params.statusId));
-  }
-
-  componentWillReceiveProps(nextProps) {
-    if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
-      this.props.dispatch(fetchReblogs(nextProps.params.statusId));
-    }
-  }
-
-  render () {
-    const { accountIds } = this.props;
-
-    if (!accountIds) {
-      return (
-        <Column>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
-    return (
-      <Column>
-        <ColumnBackButton />
-
-        <ScrollContainer scrollKey='reblogs'>
-          <div className='scrollable reblogs'>
-            {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)}
-          </div>
-        </ScrollContainer>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/report/components/status_check_box.js b/app/javascript/themes/glitch/features/report/components/status_check_box.js
deleted file mode 100644
index cc9232201..000000000
--- a/app/javascript/themes/glitch/features/report/components/status_check_box.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import Toggle from 'react-toggle';
-
-export default class StatusCheckBox extends React.PureComponent {
-
-  static propTypes = {
-    status: ImmutablePropTypes.map.isRequired,
-    checked: PropTypes.bool,
-    onToggle: PropTypes.func.isRequired,
-    disabled: PropTypes.bool,
-  };
-
-  render () {
-    const { status, checked, onToggle, disabled } = this.props;
-    const content = { __html: status.get('contentHtml') };
-
-    if (status.get('reblog')) {
-      return null;
-    }
-
-    return (
-      <div className='status-check-box'>
-        <div
-          className='status__content'
-          dangerouslySetInnerHTML={content}
-        />
-
-        <div className='status-check-box-toggle'>
-          <Toggle checked={checked} onChange={onToggle} disabled={disabled} />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/report/containers/status_check_box_container.js b/app/javascript/themes/glitch/features/report/containers/status_check_box_container.js
deleted file mode 100644
index 40d55fb3c..000000000
--- a/app/javascript/themes/glitch/features/report/containers/status_check_box_container.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import { connect } from 'react-redux';
-import StatusCheckBox from '../components/status_check_box';
-import { toggleStatusReport } from 'themes/glitch/actions/reports';
-import { Set as ImmutableSet } from 'immutable';
-
-const mapStateToProps = (state, { id }) => ({
-  status: state.getIn(['statuses', id]),
-  checked: state.getIn(['reports', 'new', 'status_ids'], ImmutableSet()).includes(id),
-});
-
-const mapDispatchToProps = (dispatch, { id }) => ({
-
-  onToggle (e) {
-    dispatch(toggleStatusReport(id, e.target.checked));
-  },
-
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(StatusCheckBox);
diff --git a/app/javascript/themes/glitch/features/standalone/compose/index.js b/app/javascript/themes/glitch/features/standalone/compose/index.js
deleted file mode 100644
index 8a8118178..000000000
--- a/app/javascript/themes/glitch/features/standalone/compose/index.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import React from 'react';
-import ComposeFormContainer from 'themes/glitch/features/compose/containers/compose_form_container';
-import NotificationsContainer from 'themes/glitch/features/ui/containers/notifications_container';
-import LoadingBarContainer from 'themes/glitch/features/ui/containers/loading_bar_container';
-import ModalContainer from 'themes/glitch/features/ui/containers/modal_container';
-
-export default class Compose extends React.PureComponent {
-
-  render () {
-    return (
-      <div>
-        <ComposeFormContainer />
-        <NotificationsContainer />
-        <ModalContainer />
-        <LoadingBarContainer className='loading-bar' />
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/standalone/hashtag_timeline/index.js b/app/javascript/themes/glitch/features/standalone/hashtag_timeline/index.js
deleted file mode 100644
index 7c56f264f..000000000
--- a/app/javascript/themes/glitch/features/standalone/hashtag_timeline/index.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import StatusListContainer from 'themes/glitch/features/ui/containers/status_list_container';
-import {
-  refreshHashtagTimeline,
-  expandHashtagTimeline,
-} from 'themes/glitch/actions/timelines';
-import Column from 'themes/glitch/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-
-@connect()
-export default class HashtagTimeline extends React.PureComponent {
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    hashtag: PropTypes.string.isRequired,
-  };
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  setRef = c => {
-    this.column = c;
-  }
-
-  componentDidMount () {
-    const { dispatch, hashtag } = this.props;
-
-    dispatch(refreshHashtagTimeline(hashtag));
-
-    this.polling = setInterval(() => {
-      dispatch(refreshHashtagTimeline(hashtag));
-    }, 10000);
-  }
-
-  componentWillUnmount () {
-    if (typeof this.polling !== 'undefined') {
-      clearInterval(this.polling);
-      this.polling = null;
-    }
-  }
-
-  handleLoadMore = () => {
-    this.props.dispatch(expandHashtagTimeline(this.props.hashtag));
-  }
-
-  render () {
-    const { hashtag } = this.props;
-
-    return (
-      <Column ref={this.setRef}>
-        <ColumnHeader
-          icon='hashtag'
-          title={hashtag}
-          onClick={this.handleHeaderClick}
-        />
-
-        <StatusListContainer
-          trackScroll={false}
-          scrollKey='standalone_hashtag_timeline'
-          timelineId={`hashtag:${hashtag}`}
-          loadMore={this.handleLoadMore}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/standalone/public_timeline/index.js b/app/javascript/themes/glitch/features/standalone/public_timeline/index.js
deleted file mode 100644
index b3fb55288..000000000
--- a/app/javascript/themes/glitch/features/standalone/public_timeline/index.js
+++ /dev/null
@@ -1,76 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import StatusListContainer from 'themes/glitch/features/ui/containers/status_list_container';
-import {
-  refreshPublicTimeline,
-  expandPublicTimeline,
-} from 'themes/glitch/actions/timelines';
-import Column from 'themes/glitch/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-import { defineMessages, injectIntl } from 'react-intl';
-
-const messages = defineMessages({
-  title: { id: 'standalone.public_title', defaultMessage: 'A look inside...' },
-});
-
-@connect()
-@injectIntl
-export default class PublicTimeline extends React.PureComponent {
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  handleHeaderClick = () => {
-    this.column.scrollTop();
-  }
-
-  setRef = c => {
-    this.column = c;
-  }
-
-  componentDidMount () {
-    const { dispatch } = this.props;
-
-    dispatch(refreshPublicTimeline());
-
-    this.polling = setInterval(() => {
-      dispatch(refreshPublicTimeline());
-    }, 3000);
-  }
-
-  componentWillUnmount () {
-    if (typeof this.polling !== 'undefined') {
-      clearInterval(this.polling);
-      this.polling = null;
-    }
-  }
-
-  handleLoadMore = () => {
-    this.props.dispatch(expandPublicTimeline());
-  }
-
-  render () {
-    const { intl } = this.props;
-
-    return (
-      <Column ref={this.setRef}>
-        <ColumnHeader
-          icon='globe'
-          title={intl.formatMessage(messages.title)}
-          onClick={this.handleHeaderClick}
-        />
-
-        <StatusListContainer
-          timelineId='public'
-          loadMore={this.handleLoadMore}
-          scrollKey='standalone_public_timeline'
-          trackScroll={false}
-        />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/status/components/action_bar.js b/app/javascript/themes/glitch/features/status/components/action_bar.js
deleted file mode 100644
index 6cda988d1..000000000
--- a/app/javascript/themes/glitch/features/status/components/action_bar.js
+++ /dev/null
@@ -1,129 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import IconButton from 'themes/glitch/components/icon_button';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import DropdownMenuContainer from 'themes/glitch/containers/dropdown_menu_container';
-import { defineMessages, injectIntl } from 'react-intl';
-import { me } from 'themes/glitch/util/initial_state';
-
-const messages = defineMessages({
-  delete: { id: 'status.delete', defaultMessage: 'Delete' },
-  mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
-  reply: { id: 'status.reply', defaultMessage: 'Reply' },
-  reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
-  cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
-  favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
-  report: { id: 'status.report', defaultMessage: 'Report @{name}' },
-  share: { id: 'status.share', defaultMessage: 'Share' },
-  pin: { id: 'status.pin', defaultMessage: 'Pin on profile' },
-  unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
-  embed: { id: 'status.embed', defaultMessage: 'Embed' },
-});
-
-@injectIntl
-export default class ActionBar extends React.PureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  static propTypes = {
-    status: ImmutablePropTypes.map.isRequired,
-    onReply: PropTypes.func.isRequired,
-    onReblog: PropTypes.func.isRequired,
-    onFavourite: PropTypes.func.isRequired,
-    onDelete: PropTypes.func.isRequired,
-    onMention: PropTypes.func.isRequired,
-    onReport: PropTypes.func,
-    onPin: PropTypes.func,
-    onEmbed: PropTypes.func,
-    intl: PropTypes.object.isRequired,
-  };
-
-  handleReplyClick = () => {
-    this.props.onReply(this.props.status);
-  }
-
-  handleReblogClick = (e) => {
-    this.props.onReblog(this.props.status, e);
-  }
-
-  handleFavouriteClick = () => {
-    this.props.onFavourite(this.props.status);
-  }
-
-  handleDeleteClick = () => {
-    this.props.onDelete(this.props.status);
-  }
-
-  handleMentionClick = () => {
-    this.props.onMention(this.props.status.get('account'), this.context.router.history);
-  }
-
-  handleReport = () => {
-    this.props.onReport(this.props.status);
-  }
-
-  handlePinClick = () => {
-    this.props.onPin(this.props.status);
-  }
-
-  handleShare = () => {
-    navigator.share({
-      text: this.props.status.get('search_index'),
-      url: this.props.status.get('url'),
-    });
-  }
-
-  handleEmbed = () => {
-    this.props.onEmbed(this.props.status);
-  }
-
-  render () {
-    const { status, intl } = this.props;
-
-    const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
-
-    let menu = [];
-
-    if (publicStatus) {
-      menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
-    }
-
-    if (me === status.getIn(['account', 'id'])) {
-      if (publicStatus) {
-        menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
-      }
-
-      menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
-    } else {
-      menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
-      menu.push(null);
-      menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
-    }
-
-    const shareButton = ('share' in navigator) && status.get('visibility') === 'public' && (
-      <div className='detailed-status__button'><IconButton title={intl.formatMessage(messages.share)} icon='share-alt' onClick={this.handleShare} /></div>
-    );
-
-    let reblogIcon = 'retweet';
-    //if (status.get('visibility') === 'direct') reblogIcon = 'envelope';
-    // else if (status.get('visibility') === 'private') reblogIcon = 'lock';
-
-    let reblog_disabled = (status.get('visibility') === 'direct' || status.get('visibility') === 'private');
-
-    return (
-      <div className='detailed-status__action-bar'>
-        <div className='detailed-status__button'><IconButton title={intl.formatMessage(messages.reply)} icon={status.get('in_reply_to_id', null) === null ? 'reply' : 'reply-all'} onClick={this.handleReplyClick} /></div>
-        <div className='detailed-status__button'><IconButton disabled={reblog_disabled} active={status.get('reblogged')} title={reblog_disabled ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(messages.reblog)} icon={reblogIcon} onClick={this.handleReblogClick} /></div>
-        <div className='detailed-status__button'><IconButton animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} activeStyle={{ color: '#ca8f04' }} /></div>
-        {shareButton}
-
-        <div className='detailed-status__action-bar-dropdown'>
-          <DropdownMenuContainer size={18} icon='ellipsis-h' items={menu} direction='left' ariaLabel='More' />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/status/components/card.js b/app/javascript/themes/glitch/features/status/components/card.js
deleted file mode 100644
index bb83374b9..000000000
--- a/app/javascript/themes/glitch/features/status/components/card.js
+++ /dev/null
@@ -1,125 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import punycode from 'punycode';
-import classnames from 'classnames';
-
-const IDNA_PREFIX = 'xn--';
-
-const decodeIDNA = domain => {
-  return domain
-    .split('.')
-    .map(part => part.indexOf(IDNA_PREFIX) === 0 ? punycode.decode(part.slice(IDNA_PREFIX.length)) : part)
-    .join('.');
-};
-
-const getHostname = url => {
-  const parser = document.createElement('a');
-  parser.href = url;
-  return parser.hostname;
-};
-
-export default class Card extends React.PureComponent {
-
-  static propTypes = {
-    card: ImmutablePropTypes.map,
-    maxDescription: PropTypes.number,
-  };
-
-  static defaultProps = {
-    maxDescription: 50,
-  };
-
-  state = {
-    width: 0,
-  };
-
-  renderLink () {
-    const { card, maxDescription } = this.props;
-
-    let image    = '';
-    let provider = card.get('provider_name');
-
-    if (card.get('image')) {
-      image = (
-        <div className='status-card__image'>
-          <img src={card.get('image')} alt={card.get('title')} className='status-card__image-image' width={card.get('width')} height={card.get('height')} />
-        </div>
-      );
-    }
-
-    if (provider.length < 1) {
-      provider = decodeIDNA(getHostname(card.get('url')));
-    }
-
-    const className = classnames('status-card', {
-      'horizontal': card.get('width') > card.get('height'),
-    });
-
-    return (
-      <a href={card.get('url')} className={className} target='_blank' rel='noopener'>
-        {image}
-
-        <div className='status-card__content'>
-          <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>
-          <p className='status-card__description'>{(card.get('description') || '').substring(0, maxDescription)}</p>
-          <span className='status-card__host'>{provider}</span>
-        </div>
-      </a>
-    );
-  }
-
-  renderPhoto () {
-    const { card } = this.props;
-
-    return (
-      <a href={card.get('url')} className='status-card-photo' target='_blank' rel='noopener'>
-        <img src={card.get('url')} alt={card.get('title')} width={card.get('width')} height={card.get('height')} />
-      </a>
-    );
-  }
-
-  setRef = c => {
-    if (c) {
-      this.setState({ width: c.offsetWidth });
-    }
-  }
-
-  renderVideo () {
-    const { card }  = this.props;
-    const content   = { __html: card.get('html') };
-    const { width } = this.state;
-    const ratio     = card.get('width') / card.get('height');
-    const height    = card.get('width') > card.get('height') ? (width / ratio) : (width * ratio);
-
-    return (
-      <div
-        ref={this.setRef}
-        className='status-card-video'
-        dangerouslySetInnerHTML={content}
-        style={{ height }}
-      />
-    );
-  }
-
-  render () {
-    const { card } = this.props;
-
-    if (card === null) {
-      return null;
-    }
-
-    switch(card.get('type')) {
-    case 'link':
-      return this.renderLink();
-    case 'photo':
-      return this.renderPhoto();
-    case 'video':
-      return this.renderVideo();
-    case 'rich':
-    default:
-      return null;
-    }
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/status/components/detailed_status.js b/app/javascript/themes/glitch/features/status/components/detailed_status.js
deleted file mode 100644
index df78ce4b6..000000000
--- a/app/javascript/themes/glitch/features/status/components/detailed_status.js
+++ /dev/null
@@ -1,130 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import Avatar from 'themes/glitch/components/avatar';
-import DisplayName from 'themes/glitch/components/display_name';
-import StatusContent from 'themes/glitch/components/status_content';
-import StatusGallery from 'themes/glitch/components/media_gallery';
-import AttachmentList from 'themes/glitch/components/attachment_list';
-import { Link } from 'react-router-dom';
-import { FormattedDate, FormattedNumber } from 'react-intl';
-import CardContainer from '../containers/card_container';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import Video from 'themes/glitch/features/video';
-import VisibilityIcon from 'themes/glitch/components/status_visibility_icon';
-
-export default class DetailedStatus extends ImmutablePureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  static propTypes = {
-    status: ImmutablePropTypes.map.isRequired,
-    settings: ImmutablePropTypes.map.isRequired,
-    onOpenMedia: PropTypes.func.isRequired,
-    onOpenVideo: PropTypes.func.isRequired,
-  };
-
-  handleAccountClick = (e) => {
-    if (e.button === 0) {
-      e.preventDefault();
-      this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
-    }
-
-    e.stopPropagation();
-  }
-
-  // handleOpenVideo = startTime => {
-  //   this.props.onOpenVideo(this.props.status.getIn(['media_attachments', 0]), startTime);
-  // }
-
-  render () {
-    const status = this.props.status.get('reblog') ? this.props.status.get('reblog') : this.props.status;
-    const { expanded, setExpansion, settings } = this.props;
-
-    let media           = '';
-    let mediaIcon       = null;
-    let applicationLink = '';
-    let reblogLink = '';
-    let reblogIcon = 'retweet';
-
-    if (status.get('media_attachments').size > 0) {
-      if (status.get('media_attachments').some(item => item.get('type') === 'unknown')) {
-        media = <AttachmentList media={status.get('media_attachments')} />;
-      } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
-        media = (
-          <Video
-            sensitive={status.get('sensitive')}
-            media={status.getIn(['media_attachments', 0])}
-            letterbox={settings.getIn(['media', 'letterbox'])}
-            fullwidth={settings.getIn(['media', 'fullwidth'])}
-            onOpenVideo={this.props.onOpenVideo}
-            autoplay
-          />
-        );
-        mediaIcon = 'video-camera';
-      } else {
-        media = (
-          <StatusGallery
-            sensitive={status.get('sensitive')}
-            media={status.get('media_attachments')}
-            letterbox={settings.getIn(['media', 'letterbox'])}
-            onOpenMedia={this.props.onOpenMedia}
-          />
-        );
-        mediaIcon = 'picture-o';
-      }
-    } else media = <CardContainer statusId={status.get('id')} />;
-
-    if (status.get('application')) {
-      applicationLink = <span> · <a className='detailed-status__application' href={status.getIn(['application', 'website'])} target='_blank' rel='noopener'>{status.getIn(['application', 'name'])}</a></span>;
-    }
-
-    if (status.get('visibility') === 'direct') {
-      reblogIcon = 'envelope';
-    } else if (status.get('visibility') === 'private') {
-      reblogIcon = 'lock';
-    }
-
-    if (status.get('visibility') === 'private') {
-      reblogLink = <i className={`fa fa-${reblogIcon}`} />;
-    } else {
-      reblogLink = (<Link to={`/statuses/${status.get('id')}/reblogs`} className='detailed-status__link'>
-        <i className={`fa fa-${reblogIcon}`} />
-        <span className='detailed-status__reblogs'>
-          <FormattedNumber value={status.get('reblogs_count')} />
-        </span>
-      </Link>);
-    }
-
-    return (
-      <div className='detailed-status'>
-        <a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='detailed-status__display-name'>
-          <div className='detailed-status__display-avatar'><Avatar account={status.get('account')} size={48} /></div>
-          <DisplayName account={status.get('account')} />
-        </a>
-
-        <StatusContent
-          status={status}
-          media={media}
-          mediaIcon={mediaIcon}
-          expanded={expanded}
-          setExpansion={setExpansion}
-        />
-
-        <div className='detailed-status__meta'>
-          <a className='detailed-status__datetime' href={status.get('url')} target='_blank' rel='noopener'>
-            <FormattedDate value={new Date(status.get('created_at'))} hour12={false} year='numeric' month='short' day='2-digit' hour='2-digit' minute='2-digit' />
-          </a>{applicationLink} · {reblogLink} · <Link to={`/statuses/${status.get('id')}/favourites`} className='detailed-status__link'>
-            <i className='fa fa-star' />
-            <span className='detailed-status__favorites'>
-              <FormattedNumber value={status.get('favourites_count')} />
-            </span>
-          </Link> · <VisibilityIcon visibility={status.get('visibility')} />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/status/containers/card_container.js b/app/javascript/themes/glitch/features/status/containers/card_container.js
deleted file mode 100644
index a97404de1..000000000
--- a/app/javascript/themes/glitch/features/status/containers/card_container.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { connect } from 'react-redux';
-import Card from '../components/card';
-
-const mapStateToProps = (state, { statusId }) => ({
-  card: state.getIn(['cards', statusId], null),
-});
-
-export default connect(mapStateToProps)(Card);
diff --git a/app/javascript/themes/glitch/features/status/index.js b/app/javascript/themes/glitch/features/status/index.js
deleted file mode 100644
index 8561bd4de..000000000
--- a/app/javascript/themes/glitch/features/status/index.js
+++ /dev/null
@@ -1,358 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { fetchStatus } from 'themes/glitch/actions/statuses';
-import MissingIndicator from 'themes/glitch/components/missing_indicator';
-import DetailedStatus from './components/detailed_status';
-import ActionBar from './components/action_bar';
-import Column from 'themes/glitch/features/ui/components/column';
-import {
-  favourite,
-  unfavourite,
-  reblog,
-  unreblog,
-  pin,
-  unpin,
-} from 'themes/glitch/actions/interactions';
-import {
-  replyCompose,
-  mentionCompose,
-} from 'themes/glitch/actions/compose';
-import { deleteStatus } from 'themes/glitch/actions/statuses';
-import { initReport } from 'themes/glitch/actions/reports';
-import { makeGetStatus } from 'themes/glitch/selectors';
-import { ScrollContainer } from 'react-router-scroll-4';
-import ColumnBackButton from 'themes/glitch/components/column_back_button';
-import StatusContainer from 'themes/glitch/containers/status_container';
-import { openModal } from 'themes/glitch/actions/modal';
-import { defineMessages, injectIntl } from 'react-intl';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { HotKeys } from 'react-hotkeys';
-import { boostModal, deleteModal } from 'themes/glitch/util/initial_state';
-import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from 'themes/glitch/util/fullscreen';
-
-const messages = defineMessages({
-  deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
-  deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
-});
-
-const makeMapStateToProps = () => {
-  const getStatus = makeGetStatus();
-
-  const mapStateToProps = (state, props) => ({
-    status: getStatus(state, props.params.statusId),
-    settings: state.get('local_settings'),
-    ancestorsIds: state.getIn(['contexts', 'ancestors', props.params.statusId]),
-    descendantsIds: state.getIn(['contexts', 'descendants', props.params.statusId]),
-  });
-
-  return mapStateToProps;
-};
-
-@injectIntl
-@connect(makeMapStateToProps)
-export default class Status extends ImmutablePureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  static propTypes = {
-    params: PropTypes.object.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    status: ImmutablePropTypes.map,
-    settings: ImmutablePropTypes.map.isRequired,
-    ancestorsIds: ImmutablePropTypes.list,
-    descendantsIds: ImmutablePropTypes.list,
-    intl: PropTypes.object.isRequired,
-  };
-
-  state = {
-    fullscreen: false,
-    isExpanded: null,
-  };
-
-  componentWillMount () {
-    this.props.dispatch(fetchStatus(this.props.params.statusId));
-  }
-
-  componentDidMount () {
-    attachFullscreenListener(this.onFullScreenChange);
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
-      this._scrolledIntoView = false;
-      this.props.dispatch(fetchStatus(nextProps.params.statusId));
-    }
-  }
-
-  handleExpandedToggle = () => {
-    if (this.props.status.get('spoiler_text')) {
-      this.setExpansion(this.state.isExpanded ? null : true);
-    }
-  };
-
-  handleFavouriteClick = (status) => {
-    if (status.get('favourited')) {
-      this.props.dispatch(unfavourite(status));
-    } else {
-      this.props.dispatch(favourite(status));
-    }
-  }
-
-  handlePin = (status) => {
-    if (status.get('pinned')) {
-      this.props.dispatch(unpin(status));
-    } else {
-      this.props.dispatch(pin(status));
-    }
-  }
-
-  handleReplyClick = (status) => {
-    this.props.dispatch(replyCompose(status, this.context.router.history));
-  }
-
-  handleModalReblog = (status) => {
-    this.props.dispatch(reblog(status));
-  }
-
-  handleReblogClick = (status, e) => {
-    if (status.get('reblogged')) {
-      this.props.dispatch(unreblog(status));
-    } else {
-      if (e.shiftKey || !boostModal) {
-        this.handleModalReblog(status);
-      } else {
-        this.props.dispatch(openModal('BOOST', { status, onReblog: this.handleModalReblog }));
-      }
-    }
-  }
-
-  handleDeleteClick = (status) => {
-    const { dispatch, intl } = this.props;
-
-    if (!deleteModal) {
-      dispatch(deleteStatus(status.get('id')));
-    } else {
-      dispatch(openModal('CONFIRM', {
-        message: intl.formatMessage(messages.deleteMessage),
-        confirm: intl.formatMessage(messages.deleteConfirm),
-        onConfirm: () => dispatch(deleteStatus(status.get('id'))),
-      }));
-    }
-  }
-
-  handleMentionClick = (account, router) => {
-    this.props.dispatch(mentionCompose(account, router));
-  }
-
-  handleOpenMedia = (media, index) => {
-    this.props.dispatch(openModal('MEDIA', { media, index }));
-  }
-
-  handleOpenVideo = (media, time) => {
-    this.props.dispatch(openModal('VIDEO', { media, time }));
-  }
-
-  handleReport = (status) => {
-    this.props.dispatch(initReport(status.get('account'), status));
-  }
-
-  handleEmbed = (status) => {
-    this.props.dispatch(openModal('EMBED', { url: status.get('url') }));
-  }
-
-  handleHotkeyMoveUp = () => {
-    this.handleMoveUp(this.props.status.get('id'));
-  }
-
-  handleHotkeyMoveDown = () => {
-    this.handleMoveDown(this.props.status.get('id'));
-  }
-
-  handleHotkeyReply = e => {
-    e.preventDefault();
-    this.handleReplyClick(this.props.status);
-  }
-
-  handleHotkeyFavourite = () => {
-    this.handleFavouriteClick(this.props.status);
-  }
-
-  handleHotkeyBoost = () => {
-    this.handleReblogClick(this.props.status);
-  }
-
-  handleHotkeyMention = e => {
-    e.preventDefault();
-    this.handleMentionClick(this.props.status);
-  }
-
-  handleHotkeyOpenProfile = () => {
-    this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
-  }
-
-  handleMoveUp = id => {
-    const { status, ancestorsIds, descendantsIds } = this.props;
-
-    if (id === status.get('id')) {
-      this._selectChild(ancestorsIds.size - 1);
-    } else {
-      let index = ancestorsIds.indexOf(id);
-
-      if (index === -1) {
-        index = descendantsIds.indexOf(id);
-        this._selectChild(ancestorsIds.size + index);
-      } else {
-        this._selectChild(index - 1);
-      }
-    }
-  }
-
-  handleMoveDown = id => {
-    const { status, ancestorsIds, descendantsIds } = this.props;
-
-    if (id === status.get('id')) {
-      this._selectChild(ancestorsIds.size + 1);
-    } else {
-      let index = ancestorsIds.indexOf(id);
-
-      if (index === -1) {
-        index = descendantsIds.indexOf(id);
-        this._selectChild(ancestorsIds.size + index + 2);
-      } else {
-        this._selectChild(index + 1);
-      }
-    }
-  }
-
-  _selectChild (index) {
-    const element = this.node.querySelectorAll('.focusable')[index];
-
-    if (element) {
-      element.focus();
-    }
-  }
-
-  renderChildren (list) {
-    return list.map(id => (
-      <StatusContainer
-        key={id}
-        id={id}
-        onMoveUp={this.handleMoveUp}
-        onMoveDown={this.handleMoveDown}
-      />
-    ));
-  }
-
-  setExpansion = value => {
-    this.setState({ isExpanded: value ? true : null });
-  }
-
-  setRef = c => {
-    this.node = c;
-  }
-
-  componentDidUpdate () {
-    if (this._scrolledIntoView) {
-      return;
-    }
-
-    const { status, ancestorsIds } = this.props;
-
-    if (status && ancestorsIds && ancestorsIds.size > 0) {
-      const element = this.node.querySelectorAll('.focusable')[ancestorsIds.size - 1];
-
-      if (element) {
-        element.scrollIntoView(true);
-        this._scrolledIntoView = true;
-      }
-    }
-  }
-
-  componentWillUnmount () {
-    detachFullscreenListener(this.onFullScreenChange);
-  }
-
-  onFullScreenChange = () => {
-    this.setState({ fullscreen: isFullscreen() });
-  }
-
-  render () {
-    let ancestors, descendants;
-    const { setExpansion } = this;
-    const { status, settings, ancestorsIds, descendantsIds } = this.props;
-    const { fullscreen, isExpanded } = this.state;
-
-    if (status === null) {
-      return (
-        <Column>
-          <ColumnBackButton />
-          <MissingIndicator />
-        </Column>
-      );
-    }
-
-    if (ancestorsIds && ancestorsIds.size > 0) {
-      ancestors = <div>{this.renderChildren(ancestorsIds)}</div>;
-    }
-
-    if (descendantsIds && descendantsIds.size > 0) {
-      descendants = <div>{this.renderChildren(descendantsIds)}</div>;
-    }
-
-    const handlers = {
-      moveUp: this.handleHotkeyMoveUp,
-      moveDown: this.handleHotkeyMoveDown,
-      reply: this.handleHotkeyReply,
-      favourite: this.handleHotkeyFavourite,
-      boost: this.handleHotkeyBoost,
-      mention: this.handleHotkeyMention,
-      openProfile: this.handleHotkeyOpenProfile,
-      toggleSpoiler: this.handleExpandedToggle,
-    };
-
-    return (
-      <Column>
-        <ColumnBackButton />
-
-        <ScrollContainer scrollKey='thread'>
-          <div className={classNames('scrollable', 'detailed-status__wrapper', { fullscreen })} ref={this.setRef}>
-            {ancestors}
-
-            <HotKeys handlers={handlers}>
-              <div className='focusable' tabIndex='0'>
-                <DetailedStatus
-                  status={status}
-                  settings={settings}
-                  onOpenVideo={this.handleOpenVideo}
-                  onOpenMedia={this.handleOpenMedia}
-                  expanded={isExpanded}
-                  setExpansion={setExpansion}
-                />
-
-                <ActionBar
-                  status={status}
-                  onReply={this.handleReplyClick}
-                  onFavourite={this.handleFavouriteClick}
-                  onReblog={this.handleReblogClick}
-                  onDelete={this.handleDeleteClick}
-                  onMention={this.handleMentionClick}
-                  onReport={this.handleReport}
-                  onPin={this.handlePin}
-                  onEmbed={this.handleEmbed}
-                />
-              </div>
-            </HotKeys>
-
-            {descendants}
-          </div>
-        </ScrollContainer>
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/actions_modal.js b/app/javascript/themes/glitch/features/ui/components/actions_modal.js
deleted file mode 100644
index 7a2b78b63..000000000
--- a/app/javascript/themes/glitch/features/ui/components/actions_modal.js
+++ /dev/null
@@ -1,74 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import StatusContent from 'themes/glitch/components/status_content';
-import Avatar from 'themes/glitch/components/avatar';
-import RelativeTimestamp from 'themes/glitch/components/relative_timestamp';
-import DisplayName from 'themes/glitch/components/display_name';
-import IconButton from 'themes/glitch/components/icon_button';
-import classNames from 'classnames';
-
-export default class ActionsModal extends ImmutablePureComponent {
-
-  static propTypes = {
-    status: ImmutablePropTypes.map,
-    actions: PropTypes.array,
-    onClick: PropTypes.func,
-  };
-
-  renderAction = (action, i) => {
-    if (action === null) {
-      return <li key={`sep-${i}`} className='dropdown-menu__separator' />;
-    }
-
-    const { icon = null, text, meta = null, active = false, href = '#' } = action;
-
-    return (
-      <li key={`${text}-${i}`}>
-        <a href={href} target='_blank' rel='noopener' onClick={this.props.onClick} data-index={i} className={classNames({ active })}>
-          {icon && <IconButton title={text} icon={icon} role='presentation' tabIndex='-1' />}
-          <div>
-            <div className={classNames({ 'actions-modal__item-label': !!meta })}>{text}</div>
-            <div>{meta}</div>
-          </div>
-        </a>
-      </li>
-    );
-  }
-
-  render () {
-    const status = this.props.status && (
-      <div className='status light'>
-        <div className='boost-modal__status-header'>
-          <div className='boost-modal__status-time'>
-            <a href={this.props.status.get('url')} className='status__relative-time' target='_blank' rel='noopener'>
-              <RelativeTimestamp timestamp={this.props.status.get('created_at')} />
-            </a>
-          </div>
-
-          <a href={this.props.status.getIn(['account', 'url'])} className='status__display-name'>
-            <div className='status__avatar'>
-              <Avatar account={this.props.status.get('account')} size={48} />
-            </div>
-
-            <DisplayName account={this.props.status.get('account')} />
-          </a>
-        </div>
-
-        <StatusContent status={this.props.status} />
-      </div>
-    );
-
-    return (
-      <div className='modal-root__modal actions-modal'>
-        {status}
-
-        <ul>
-          {this.props.actions.map(this.renderAction)}
-        </ul>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/boost_modal.js b/app/javascript/themes/glitch/features/ui/components/boost_modal.js
deleted file mode 100644
index 49781db10..000000000
--- a/app/javascript/themes/glitch/features/ui/components/boost_modal.js
+++ /dev/null
@@ -1,84 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import Button from 'themes/glitch/components/button';
-import StatusContent from 'themes/glitch/components/status_content';
-import Avatar from 'themes/glitch/components/avatar';
-import RelativeTimestamp from 'themes/glitch/components/relative_timestamp';
-import DisplayName from 'themes/glitch/components/display_name';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-const messages = defineMessages({
-  reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
-});
-
-@injectIntl
-export default class BoostModal extends ImmutablePureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object,
-  };
-
-  static propTypes = {
-    status: ImmutablePropTypes.map.isRequired,
-    onReblog: PropTypes.func.isRequired,
-    onClose: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  componentDidMount() {
-    this.button.focus();
-  }
-
-  handleReblog = () => {
-    this.props.onReblog(this.props.status);
-    this.props.onClose();
-  }
-
-  handleAccountClick = (e) => {
-    if (e.button === 0) {
-      e.preventDefault();
-      this.props.onClose();
-      this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
-    }
-  }
-
-  setRef = (c) => {
-    this.button = c;
-  }
-
-  render () {
-    const { status, intl } = this.props;
-
-    return (
-      <div className='modal-root__modal boost-modal'>
-        <div className='boost-modal__container'>
-          <div className='status light'>
-            <div className='boost-modal__status-header'>
-              <div className='boost-modal__status-time'>
-                <a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'><RelativeTimestamp timestamp={status.get('created_at')} /></a>
-              </div>
-
-              <a onClick={this.handleAccountClick} href={status.getIn(['account', 'url'])} className='status__display-name'>
-                <div className='status__avatar'>
-                  <Avatar account={status.get('account')} size={48} />
-                </div>
-
-                <DisplayName account={status.get('account')} />
-              </a>
-            </div>
-
-            <StatusContent status={status} />
-          </div>
-        </div>
-
-        <div className='boost-modal__action-bar'>
-          <div><FormattedMessage id='boost_modal.combo' defaultMessage='You can press {combo} to skip this next time' values={{ combo: <span>Shift + <i className='fa fa-retweet' /></span> }} /></div>
-          <Button text={intl.formatMessage(messages.reblog)} onClick={this.handleReblog} ref={this.setRef} />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/bundle.js b/app/javascript/themes/glitch/features/ui/components/bundle.js
deleted file mode 100644
index fc88e0c70..000000000
--- a/app/javascript/themes/glitch/features/ui/components/bundle.js
+++ /dev/null
@@ -1,102 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-const emptyComponent = () => null;
-const noop = () => { };
-
-class Bundle extends React.Component {
-
-  static propTypes = {
-    fetchComponent: PropTypes.func.isRequired,
-    loading: PropTypes.func,
-    error: PropTypes.func,
-    children: PropTypes.func.isRequired,
-    renderDelay: PropTypes.number,
-    onFetch: PropTypes.func,
-    onFetchSuccess: PropTypes.func,
-    onFetchFail: PropTypes.func,
-  }
-
-  static defaultProps = {
-    loading: emptyComponent,
-    error: emptyComponent,
-    renderDelay: 0,
-    onFetch: noop,
-    onFetchSuccess: noop,
-    onFetchFail: noop,
-  }
-
-  static cache = {}
-
-  state = {
-    mod: undefined,
-    forceRender: false,
-  }
-
-  componentWillMount() {
-    this.load(this.props);
-  }
-
-  componentWillReceiveProps(nextProps) {
-    if (nextProps.fetchComponent !== this.props.fetchComponent) {
-      this.load(nextProps);
-    }
-  }
-
-  componentWillUnmount () {
-    if (this.timeout) {
-      clearTimeout(this.timeout);
-    }
-  }
-
-  load = (props) => {
-    const { fetchComponent, onFetch, onFetchSuccess, onFetchFail, renderDelay } = props || this.props;
-
-    onFetch();
-
-    if (Bundle.cache[fetchComponent.name]) {
-      const mod = Bundle.cache[fetchComponent.name];
-
-      this.setState({ mod: mod.default });
-      onFetchSuccess();
-      return Promise.resolve();
-    }
-
-    this.setState({ mod: undefined });
-
-    if (renderDelay !== 0) {
-      this.timestamp = new Date();
-      this.timeout = setTimeout(() => this.setState({ forceRender: true }), renderDelay);
-    }
-
-    return fetchComponent()
-      .then((mod) => {
-        Bundle.cache[fetchComponent.name] = mod;
-        this.setState({ mod: mod.default });
-        onFetchSuccess();
-      })
-      .catch((error) => {
-        this.setState({ mod: null });
-        onFetchFail(error);
-      });
-  }
-
-  render() {
-    const { loading: Loading, error: Error, children, renderDelay } = this.props;
-    const { mod, forceRender } = this.state;
-    const elapsed = this.timestamp ? (new Date() - this.timestamp) : renderDelay;
-
-    if (mod === undefined) {
-      return (elapsed >= renderDelay || forceRender) ? <Loading /> : null;
-    }
-
-    if (mod === null) {
-      return <Error onRetry={this.load} />;
-    }
-
-    return children(mod);
-  }
-
-}
-
-export default Bundle;
diff --git a/app/javascript/themes/glitch/features/ui/components/bundle_column_error.js b/app/javascript/themes/glitch/features/ui/components/bundle_column_error.js
deleted file mode 100644
index daedc6299..000000000
--- a/app/javascript/themes/glitch/features/ui/components/bundle_column_error.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl } from 'react-intl';
-
-import Column from './column';
-import ColumnHeader from './column_header';
-import ColumnBackButtonSlim from 'themes/glitch/components/column_back_button_slim';
-import IconButton from 'themes/glitch/components/icon_button';
-
-const messages = defineMessages({
-  title: { id: 'bundle_column_error.title', defaultMessage: 'Network error' },
-  body: { id: 'bundle_column_error.body', defaultMessage: 'Something went wrong while loading this component.' },
-  retry: { id: 'bundle_column_error.retry', defaultMessage: 'Try again' },
-});
-
-class BundleColumnError extends React.Component {
-
-  static propTypes = {
-    onRetry: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  }
-
-  handleRetry = () => {
-    this.props.onRetry();
-  }
-
-  render () {
-    const { intl: { formatMessage } } = this.props;
-
-    return (
-      <Column>
-        <ColumnHeader icon='exclamation-circle' type={formatMessage(messages.title)} />
-        <ColumnBackButtonSlim />
-        <div className='error-column'>
-          <IconButton title={formatMessage(messages.retry)} icon='refresh' onClick={this.handleRetry} size={64} />
-          {formatMessage(messages.body)}
-        </div>
-      </Column>
-    );
-  }
-
-}
-
-export default injectIntl(BundleColumnError);
diff --git a/app/javascript/themes/glitch/features/ui/components/bundle_modal_error.js b/app/javascript/themes/glitch/features/ui/components/bundle_modal_error.js
deleted file mode 100644
index 8cca32ae9..000000000
--- a/app/javascript/themes/glitch/features/ui/components/bundle_modal_error.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl } from 'react-intl';
-
-import IconButton from 'themes/glitch/components/icon_button';
-
-const messages = defineMessages({
-  error: { id: 'bundle_modal_error.message', defaultMessage: 'Something went wrong while loading this component.' },
-  retry: { id: 'bundle_modal_error.retry', defaultMessage: 'Try again' },
-  close: { id: 'bundle_modal_error.close', defaultMessage: 'Close' },
-});
-
-class BundleModalError extends React.Component {
-
-  static propTypes = {
-    onRetry: PropTypes.func.isRequired,
-    onClose: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  }
-
-  handleRetry = () => {
-    this.props.onRetry();
-  }
-
-  render () {
-    const { onClose, intl: { formatMessage } } = this.props;
-
-    // Keep the markup in sync with <ModalLoading />
-    // (make sure they have the same dimensions)
-    return (
-      <div className='modal-root__modal error-modal'>
-        <div className='error-modal__body'>
-          <IconButton title={formatMessage(messages.retry)} icon='refresh' onClick={this.handleRetry} size={64} />
-          {formatMessage(messages.error)}
-        </div>
-
-        <div className='error-modal__footer'>
-          <div>
-            <button
-              onClick={onClose}
-              className='error-modal__nav onboarding-modal__skip'
-            >
-              {formatMessage(messages.close)}
-            </button>
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
-
-export default injectIntl(BundleModalError);
diff --git a/app/javascript/themes/glitch/features/ui/components/column.js b/app/javascript/themes/glitch/features/ui/components/column.js
deleted file mode 100644
index 73a5bc15e..000000000
--- a/app/javascript/themes/glitch/features/ui/components/column.js
+++ /dev/null
@@ -1,74 +0,0 @@
-import React from 'react';
-import ColumnHeader from './column_header';
-import PropTypes from 'prop-types';
-import { debounce } from 'lodash';
-import { scrollTop } from 'themes/glitch/util/scroll';
-import { isMobile } from 'themes/glitch/util/is_mobile';
-
-export default class Column extends React.PureComponent {
-
-  static propTypes = {
-    heading: PropTypes.string,
-    icon: PropTypes.string,
-    children: PropTypes.node,
-    active: PropTypes.bool,
-    hideHeadingOnMobile: PropTypes.bool,
-    name: PropTypes.string,
-  };
-
-  handleHeaderClick = () => {
-    const scrollable = this.node.querySelector('.scrollable');
-
-    if (!scrollable) {
-      return;
-    }
-
-    this._interruptScrollAnimation = scrollTop(scrollable);
-  }
-
-  scrollTop () {
-    const scrollable = this.node.querySelector('.scrollable');
-
-    if (!scrollable) {
-      return;
-    }
-
-    this._interruptScrollAnimation = scrollTop(scrollable);
-  }
-
-
-  handleScroll = debounce(() => {
-    if (typeof this._interruptScrollAnimation !== 'undefined') {
-      this._interruptScrollAnimation();
-    }
-  }, 200)
-
-  setRef = (c) => {
-    this.node = c;
-  }
-
-  render () {
-    const { heading, icon, children, active, hideHeadingOnMobile, name } = this.props;
-
-    const showHeading = heading && (!hideHeadingOnMobile || (hideHeadingOnMobile && !isMobile(window.innerWidth)));
-
-    const columnHeaderId = showHeading && heading.replace(/ /g, '-');
-    const header = showHeading && (
-      <ColumnHeader icon={icon} active={active} type={heading} onClick={this.handleHeaderClick} columnHeaderId={columnHeaderId} />
-    );
-    return (
-      <div
-        ref={this.setRef}
-        role='region'
-        data-column={name}
-        aria-labelledby={columnHeaderId}
-        className='column'
-        onScroll={this.handleScroll}
-      >
-        {header}
-        {children}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/column_header.js b/app/javascript/themes/glitch/features/ui/components/column_header.js
deleted file mode 100644
index af195ea9c..000000000
--- a/app/javascript/themes/glitch/features/ui/components/column_header.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-export default class ColumnHeader extends React.PureComponent {
-
-  static propTypes = {
-    icon: PropTypes.string,
-    type: PropTypes.string,
-    active: PropTypes.bool,
-    onClick: PropTypes.func,
-    columnHeaderId: PropTypes.string,
-  };
-
-  handleClick = () => {
-    this.props.onClick();
-  }
-
-  render () {
-    const { type, active, columnHeaderId } = this.props;
-
-    let icon = '';
-
-    if (this.props.icon) {
-      icon = <i className={`fa fa-fw fa-${this.props.icon} column-header__icon`} />;
-    }
-
-    return (
-      <div role='heading' tabIndex='0' className={`column-header ${active ? 'active' : ''}`} onClick={this.handleClick} id={columnHeaderId || null}>
-        {icon}
-        {type}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/column_link.js b/app/javascript/themes/glitch/features/ui/components/column_link.js
deleted file mode 100644
index b845d1895..000000000
--- a/app/javascript/themes/glitch/features/ui/components/column_link.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { Link } from 'react-router-dom';
-
-const ColumnLink = ({ icon, text, to, onClick, href, method }) => {
-  if (href) {
-    return (
-      <a href={href} className='column-link' data-method={method}>
-        <i className={`fa fa-fw fa-${icon} column-link__icon`} />
-        {text}
-      </a>
-    );
-  } else if (to) {
-    return (
-      <Link to={to} className='column-link'>
-        <i className={`fa fa-fw fa-${icon} column-link__icon`} />
-        {text}
-      </Link>
-    );
-  } else {
-    return (
-      <a onClick={onClick} className='column-link' role='button' tabIndex='0' data-method={method}>
-        <i className={`fa fa-fw fa-${icon} column-link__icon`} />
-        {text}
-      </a>
-    );
-  }
-};
-
-ColumnLink.propTypes = {
-  icon: PropTypes.string.isRequired,
-  text: PropTypes.string.isRequired,
-  to: PropTypes.string,
-  onClick: PropTypes.func,
-  href: PropTypes.string,
-  method: PropTypes.string,
-};
-
-export default ColumnLink;
diff --git a/app/javascript/themes/glitch/features/ui/components/column_loading.js b/app/javascript/themes/glitch/features/ui/components/column_loading.js
deleted file mode 100644
index 75f26218a..000000000
--- a/app/javascript/themes/glitch/features/ui/components/column_loading.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import Column from 'themes/glitch/components/column';
-import ColumnHeader from 'themes/glitch/components/column_header';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-export default class ColumnLoading extends ImmutablePureComponent {
-
-  static propTypes = {
-    title: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
-    icon: PropTypes.string,
-  };
-
-  static defaultProps = {
-    title: '',
-    icon: '',
-  };
-
-  render() {
-    let { title, icon } = this.props;
-    return (
-      <Column>
-        <ColumnHeader icon={icon} title={title} multiColumn={false} focusable={false} />
-        <div className='scrollable' />
-      </Column>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/column_subheading.js b/app/javascript/themes/glitch/features/ui/components/column_subheading.js
deleted file mode 100644
index 8160c4aa3..000000000
--- a/app/javascript/themes/glitch/features/ui/components/column_subheading.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-const ColumnSubheading = ({ text }) => {
-  return (
-    <div className='column-subheading'>
-      {text}
-    </div>
-  );
-};
-
-ColumnSubheading.propTypes = {
-  text: PropTypes.string.isRequired,
-};
-
-export default ColumnSubheading;
diff --git a/app/javascript/themes/glitch/features/ui/components/columns_area.js b/app/javascript/themes/glitch/features/ui/components/columns_area.js
deleted file mode 100644
index 452950363..000000000
--- a/app/javascript/themes/glitch/features/ui/components/columns_area.js
+++ /dev/null
@@ -1,174 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { injectIntl } from 'react-intl';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-import ReactSwipeableViews from 'react-swipeable-views';
-import { links, getIndex, getLink } from './tabs_bar';
-
-import BundleContainer from '../containers/bundle_container';
-import ColumnLoading from './column_loading';
-import DrawerLoading from './drawer_loading';
-import BundleColumnError from './bundle_column_error';
-import { Compose, Notifications, HomeTimeline, CommunityTimeline, PublicTimeline, HashtagTimeline, DirectTimeline, FavouritedStatuses } from 'themes/glitch/util/async-components';
-
-import detectPassiveEvents from 'detect-passive-events';
-import { scrollRight } from 'themes/glitch/util/scroll';
-
-const componentMap = {
-  'COMPOSE': Compose,
-  'HOME': HomeTimeline,
-  'NOTIFICATIONS': Notifications,
-  'PUBLIC': PublicTimeline,
-  'COMMUNITY': CommunityTimeline,
-  'HASHTAG': HashtagTimeline,
-  'DIRECT': DirectTimeline,
-  'FAVOURITES': FavouritedStatuses,
-};
-
-@component => injectIntl(component, { withRef: true })
-export default class ColumnsArea extends ImmutablePureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object.isRequired,
-  };
-
-  static propTypes = {
-    intl: PropTypes.object.isRequired,
-    columns: ImmutablePropTypes.list.isRequired,
-    singleColumn: PropTypes.bool,
-    children: PropTypes.node,
-  };
-
-  state = {
-    shouldAnimate: false,
-  }
-
-  componentWillReceiveProps() {
-    this.setState({ shouldAnimate: false });
-  }
-
-  componentDidMount() {
-    if (!this.props.singleColumn) {
-      this.node.addEventListener('wheel', this.handleWheel,  detectPassiveEvents.hasSupport ? { passive: true } : false);
-    }
-    this.lastIndex = getIndex(this.context.router.history.location.pathname);
-    this.setState({ shouldAnimate: true });
-  }
-
-  componentWillUpdate(nextProps) {
-    if (this.props.singleColumn !== nextProps.singleColumn && nextProps.singleColumn) {
-      this.node.removeEventListener('wheel', this.handleWheel);
-    }
-  }
-
-  componentDidUpdate(prevProps) {
-    if (this.props.singleColumn !== prevProps.singleColumn && !this.props.singleColumn) {
-      this.node.addEventListener('wheel', this.handleWheel,  detectPassiveEvents.hasSupport ? { passive: true } : false);
-    }
-    this.lastIndex = getIndex(this.context.router.history.location.pathname);
-    this.setState({ shouldAnimate: true });
-  }
-
-  componentWillUnmount () {
-    if (!this.props.singleColumn) {
-      this.node.removeEventListener('wheel', this.handleWheel);
-    }
-  }
-
-  handleChildrenContentChange() {
-    if (!this.props.singleColumn) {
-      this._interruptScrollAnimation = scrollRight(this.node, this.node.scrollWidth - window.innerWidth);
-    }
-  }
-
-  handleSwipe = (index) => {
-    this.pendingIndex = index;
-
-    const nextLinkTranslationId = links[index].props['data-preview-title-id'];
-    const currentLinkSelector = '.tabs-bar__link.active';
-    const nextLinkSelector = `.tabs-bar__link[data-preview-title-id="${nextLinkTranslationId}"]`;
-
-    // HACK: Remove the active class from the current link and set it to the next one
-    // React-router does this for us, but too late, feeling laggy.
-    document.querySelector(currentLinkSelector).classList.remove('active');
-    document.querySelector(nextLinkSelector).classList.add('active');
-  }
-
-  handleAnimationEnd = () => {
-    if (typeof this.pendingIndex === 'number') {
-      this.context.router.history.push(getLink(this.pendingIndex));
-      this.pendingIndex = null;
-    }
-  }
-
-  handleWheel = () => {
-    if (typeof this._interruptScrollAnimation !== 'function') {
-      return;
-    }
-
-    this._interruptScrollAnimation();
-  }
-
-  setRef = (node) => {
-    this.node = node;
-  }
-
-  renderView = (link, index) => {
-    const columnIndex = getIndex(this.context.router.history.location.pathname);
-    const title = this.props.intl.formatMessage({ id: link.props['data-preview-title-id'] });
-    const icon = link.props['data-preview-icon'];
-
-    const view = (index === columnIndex) ?
-      React.cloneElement(this.props.children) :
-      <ColumnLoading title={title} icon={icon} />;
-
-    return (
-      <div className='columns-area' key={index}>
-        {view}
-      </div>
-    );
-  }
-
-  renderLoading = columnId => () => {
-    return columnId === 'COMPOSE' ? <DrawerLoading /> : <ColumnLoading />;
-  }
-
-  renderError = (props) => {
-    return <BundleColumnError {...props} />;
-  }
-
-  render () {
-    const { columns, children, singleColumn } = this.props;
-    const { shouldAnimate } = this.state;
-
-    const columnIndex = getIndex(this.context.router.history.location.pathname);
-    this.pendingIndex = null;
-
-    if (singleColumn) {
-      return columnIndex !== -1 ? (
-        <ReactSwipeableViews index={columnIndex} onChangeIndex={this.handleSwipe} onTransitionEnd={this.handleAnimationEnd} animateTransitions={shouldAnimate} springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }} style={{ height: '100%' }}>
-          {links.map(this.renderView)}
-        </ReactSwipeableViews>
-      ) : <div className='columns-area'>{children}</div>;
-    }
-
-    return (
-      <div className='columns-area' ref={this.setRef}>
-        {columns.map(column => {
-          const params = column.get('params', null) === null ? null : column.get('params').toJS();
-
-          return (
-            <BundleContainer key={column.get('uuid')} fetchComponent={componentMap[column.get('id')]} loading={this.renderLoading(column.get('id'))} error={this.renderError}>
-              {SpecificComponent => <SpecificComponent columnId={column.get('uuid')} params={params} multiColumn />}
-            </BundleContainer>
-          );
-        })}
-
-        {React.Children.map(children, child => React.cloneElement(child, { multiColumn: true }))}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/confirmation_modal.js b/app/javascript/themes/glitch/features/ui/components/confirmation_modal.js
deleted file mode 100644
index 3d568aec3..000000000
--- a/app/javascript/themes/glitch/features/ui/components/confirmation_modal.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { injectIntl, FormattedMessage } from 'react-intl';
-import Button from 'themes/glitch/components/button';
-
-@injectIntl
-export default class ConfirmationModal extends React.PureComponent {
-
-  static propTypes = {
-    message: PropTypes.node.isRequired,
-    confirm: PropTypes.string.isRequired,
-    onClose: PropTypes.func.isRequired,
-    onConfirm: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  componentDidMount() {
-    this.button.focus();
-  }
-
-  handleClick = () => {
-    this.props.onClose();
-    this.props.onConfirm();
-  }
-
-  handleCancel = () => {
-    this.props.onClose();
-  }
-
-  setRef = (c) => {
-    this.button = c;
-  }
-
-  render () {
-    const { message, confirm } = this.props;
-
-    return (
-      <div className='modal-root__modal confirmation-modal'>
-        <div className='confirmation-modal__container'>
-          {message}
-        </div>
-
-        <div className='confirmation-modal__action-bar'>
-          <Button onClick={this.handleCancel} className='confirmation-modal__cancel-button'>
-            <FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' />
-          </Button>
-          <Button text={confirm} onClick={this.handleClick} ref={this.setRef} />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/doodle_modal.js b/app/javascript/themes/glitch/features/ui/components/doodle_modal.js
deleted file mode 100644
index 819656dbf..000000000
--- a/app/javascript/themes/glitch/features/ui/components/doodle_modal.js
+++ /dev/null
@@ -1,614 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Button from 'themes/glitch/components/button';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import Atrament from 'atrament'; // the doodling library
-import { connect } from 'react-redux';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { doodleSet, uploadCompose } from 'themes/glitch/actions/compose';
-import IconButton from 'themes/glitch/components/icon_button';
-import { debounce, mapValues } from 'lodash';
-import classNames from 'classnames';
-
-// palette nicked from MyPaint, CC0
-const palette = [
-  ['rgb(  0,    0,    0)', 'Black'],
-  ['rgb( 38,   38,   38)', 'Gray 15'],
-  ['rgb( 77,   77,   77)', 'Grey 30'],
-  ['rgb(128,  128,  128)', 'Grey 50'],
-  ['rgb(171,  171,  171)', 'Grey 67'],
-  ['rgb(217,  217,  217)', 'Grey 85'],
-  ['rgb(255,  255,  255)', 'White'],
-  ['rgb(128,    0,    0)', 'Maroon'],
-  ['rgb(209,    0,    0)', 'English-red'],
-  ['rgb(255,   54,   34)', 'Tomato'],
-  ['rgb(252,   60,    3)', 'Orange-red'],
-  ['rgb(255,  140,  105)', 'Salmon'],
-  ['rgb(252,  232,   32)', 'Cadium-yellow'],
-  ['rgb(243,  253,   37)', 'Lemon yellow'],
-  ['rgb(121,    5,   35)', 'Dark crimson'],
-  ['rgb(169,   32,   62)', 'Deep carmine'],
-  ['rgb(255,  140,    0)', 'Orange'],
-  ['rgb(255,  168,   18)', 'Dark tangerine'],
-  ['rgb(217,  144,   88)', 'Persian orange'],
-  ['rgb(194,  178,  128)', 'Sand'],
-  ['rgb(255,  229,  180)', 'Peach'],
-  ['rgb(100,   54,   46)', 'Bole'],
-  ['rgb(108,   41,   52)', 'Dark cordovan'],
-  ['rgb(163,   65,   44)', 'Chestnut'],
-  ['rgb(228,  136,  100)', 'Dark salmon'],
-  ['rgb(255,  195,  143)', 'Apricot'],
-  ['rgb(255,  219,  188)', 'Unbleached silk'],
-  ['rgb(242,  227,  198)', 'Straw'],
-  ['rgb( 53,   19,   13)', 'Bistre'],
-  ['rgb( 84,   42,   14)', 'Dark chocolate'],
-  ['rgb(102,   51,   43)', 'Burnt sienna'],
-  ['rgb(184,   66,    0)', 'Sienna'],
-  ['rgb(216,  153,   12)', 'Yellow ochre'],
-  ['rgb(210,  180,  140)', 'Tan'],
-  ['rgb(232,  204,  144)', 'Dark wheat'],
-  ['rgb(  0,   49,   83)', 'Prussian blue'],
-  ['rgb( 48,   69,  119)', 'Dark grey blue'],
-  ['rgb(  0,   71,  171)', 'Cobalt blue'],
-  ['rgb( 31,  117,  254)', 'Blue'],
-  ['rgb(120,  180,  255)', 'Bright french blue'],
-  ['rgb(171,  200,  255)', 'Bright steel blue'],
-  ['rgb(208,  231,  255)', 'Ice blue'],
-  ['rgb( 30,   51,   58)', 'Medium jungle green'],
-  ['rgb( 47,   79,   79)', 'Dark slate grey'],
-  ['rgb( 74,  104,   93)', 'Dark grullo green'],
-  ['rgb(  0,  128,  128)', 'Teal'],
-  ['rgb( 67,  170,  176)', 'Turquoise'],
-  ['rgb(109,  174,  199)', 'Cerulean frost'],
-  ['rgb(173,  217,  186)', 'Tiffany green'],
-  ['rgb( 22,   34,   29)', 'Gray-asparagus'],
-  ['rgb( 36,   48,   45)', 'Medium dark teal'],
-  ['rgb( 74,  104,   93)', 'Xanadu'],
-  ['rgb(119,  198,  121)', 'Mint'],
-  ['rgb(175,  205,  182)', 'Timberwolf'],
-  ['rgb(185,  245,  246)', 'Celeste'],
-  ['rgb(193,  255,  234)', 'Aquamarine'],
-  ['rgb( 29,   52,   35)', 'Cal Poly Pomona'],
-  ['rgb(  1,   68,   33)', 'Forest green'],
-  ['rgb( 42,  128,    0)', 'Napier green'],
-  ['rgb(128,  128,    0)', 'Olive'],
-  ['rgb( 65,  156,  105)', 'Sea green'],
-  ['rgb(189,  246,   29)', 'Green-yellow'],
-  ['rgb(231,  244,  134)', 'Bright chartreuse'],
-  ['rgb(138,   23,  137)', 'Purple'],
-  ['rgb( 78,   39,  138)', 'Violet'],
-  ['rgb(193,   75,  110)', 'Dark thulian pink'],
-  ['rgb(222,   49,   99)', 'Cerise'],
-  ['rgb(255,   20,  147)', 'Deep pink'],
-  ['rgb(255,  102,  204)', 'Rose pink'],
-  ['rgb(255,  203,  219)', 'Pink'],
-  ['rgb(255,  255,  255)', 'White'],
-  ['rgb(229,   17,    1)', 'RGB Red'],
-  ['rgb(  0,  255,    0)', 'RGB Green'],
-  ['rgb(  0,    0,  255)', 'RGB Blue'],
-  ['rgb(  0,  255,  255)', 'CMYK Cyan'],
-  ['rgb(255,    0,  255)', 'CMYK Magenta'],
-  ['rgb(255,  255,    0)', 'CMYK Yellow'],
-];
-
-// re-arrange to the right order for display
-let palReordered = [];
-for (let row = 0; row < 7; row++) {
-  for (let col = 0; col < 11; col++) {
-    palReordered.push(palette[col * 7 + row]);
-  }
-  palReordered.push(null); // null indicates a <br />
-}
-
-// Utility for converting base64 image to binary for upload
-// https://stackoverflow.com/questions/35940290/how-to-convert-base64-string-to-javascript-file-object-like-as-from-file-input-f
-function dataURLtoFile(dataurl, filename) {
-  let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
-    bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
-  while(n--){
-    u8arr[n] = bstr.charCodeAt(n);
-  }
-  return new File([u8arr], filename, { type: mime });
-}
-
-const DOODLE_SIZES = {
-  normal: [500, 500, 'Square 500'],
-  tootbanner: [702, 330, 'Tootbanner'],
-  s640x480: [640, 480, '640×480 - 480p'],
-  s800x600: [800, 600, '800×600 - SVGA'],
-  s720x480: [720, 405, '720x405 - 16:9'],
-};
-
-
-const mapStateToProps = state => ({
-  options: state.getIn(['compose', 'doodle']),
-});
-
-const mapDispatchToProps = dispatch => ({
-  /** Set options in the redux store */
-  setOpt: (opts) => dispatch(doodleSet(opts)),
-  /** Submit doodle for upload */
-  submit: (file) => dispatch(uploadCompose([file])),
-});
-
-/**
- * Doodling dialog with drawing canvas
- *
- * Keyboard shortcuts:
- * - Delete: Clear screen, fill with background color
- * - Backspace, Ctrl+Z: Undo one step
- * - Ctrl held while drawing: Use background color
- * - Shift held while clicking screen: Use fill tool
- *
- * Palette:
- * - Left mouse button: pick foreground
- * - Ctrl + left mouse button: pick background
- * - Right mouse button: pick background
- */
-@connect(mapStateToProps, mapDispatchToProps)
-export default class DoodleModal extends ImmutablePureComponent {
-
-  static propTypes = {
-    options: ImmutablePropTypes.map,
-    onClose: PropTypes.func.isRequired,
-    setOpt: PropTypes.func.isRequired,
-    submit: PropTypes.func.isRequired,
-  };
-
-  //region Option getters/setters
-
-  /** Foreground color */
-  get fg () {
-    return this.props.options.get('fg');
-  }
-  set fg (value) {
-    this.props.setOpt({ fg: value });
-  }
-
-  /** Background color */
-  get bg () {
-    return this.props.options.get('bg');
-  }
-  set bg (value) {
-    this.props.setOpt({ bg: value });
-  }
-
-  /** Swap Fg and Bg for drawing */
-  get swapped () {
-    return this.props.options.get('swapped');
-  }
-  set swapped (value) {
-    this.props.setOpt({ swapped: value });
-  }
-
-  /** Mode - 'draw' or 'fill' */
-  get mode () {
-    return this.props.options.get('mode');
-  }
-  set mode (value) {
-    this.props.setOpt({ mode: value });
-  }
-
-  /** Base line weight */
-  get weight () {
-    return this.props.options.get('weight');
-  }
-  set weight (value) {
-    this.props.setOpt({ weight: value });
-  }
-
-  /** Drawing opacity */
-  get opacity () {
-    return this.props.options.get('opacity');
-  }
-  set opacity (value) {
-    this.props.setOpt({ opacity: value });
-  }
-
-  /** Adaptive stroke - change width with speed */
-  get adaptiveStroke () {
-    return this.props.options.get('adaptiveStroke');
-  }
-  set adaptiveStroke (value) {
-    this.props.setOpt({ adaptiveStroke: value });
-  }
-
-  /** Smoothing (for mouse drawing) */
-  get smoothing () {
-    return this.props.options.get('smoothing');
-  }
-  set smoothing (value) {
-    this.props.setOpt({ smoothing: value });
-  }
-
-  /** Size preset */
-  get size () {
-    return this.props.options.get('size');
-  }
-  set size (value) {
-    this.props.setOpt({ size: value });
-  }
-
-  //endregion
-
-  /** Key up handler */
-  handleKeyUp = (e) => {
-    if (e.target.nodeName === 'INPUT') return;
-
-    if (e.key === 'Delete') {
-      e.preventDefault();
-      this.handleClearBtn();
-      return;
-    }
-
-    if (e.key === 'Backspace' || (e.key === 'z' && (e.ctrlKey || e.metaKey))) {
-      e.preventDefault();
-      this.undo();
-    }
-
-    if (e.key === 'Control' || e.key === 'Meta') {
-      this.controlHeld = false;
-      this.swapped = false;
-    }
-
-    if (e.key === 'Shift') {
-      this.shiftHeld = false;
-      this.mode = 'draw';
-    }
-  };
-
-  /** Key down handler */
-  handleKeyDown = (e) => {
-    if (e.key === 'Control' || e.key === 'Meta') {
-      this.controlHeld = true;
-      this.swapped = true;
-    }
-
-    if (e.key === 'Shift') {
-      this.shiftHeld = true;
-      this.mode = 'fill';
-    }
-  };
-
-  /**
-   * Component installed in the DOM, do some initial set-up
-   */
-  componentDidMount () {
-    this.controlHeld = false;
-    this.shiftHeld = false;
-    this.swapped = false;
-    window.addEventListener('keyup', this.handleKeyUp, false);
-    window.addEventListener('keydown', this.handleKeyDown, false);
-  };
-
-  /**
-   * Tear component down
-   */
-  componentWillUnmount () {
-    window.removeEventListener('keyup', this.handleKeyUp, false);
-    window.removeEventListener('keydown', this.handleKeyDown, false);
-    if (this.sketcher) this.sketcher.destroy();
-  }
-
-  /**
-   * Set reference to the canvas element.
-   * This is called during component init
-   *
-   * @param elem - canvas element
-   */
-  setCanvasRef = (elem) => {
-    this.canvas = elem;
-    if (elem) {
-      elem.addEventListener('dirty', () => {
-        this.saveUndo();
-        this.sketcher._dirty = false;
-      });
-
-      elem.addEventListener('click', () => {
-        // sketcher bug - does not fire dirty on fill
-        if (this.mode === 'fill') {
-          this.saveUndo();
-        }
-      });
-
-      // prevent context menu
-      elem.addEventListener('contextmenu', (e) => {
-        e.preventDefault();
-      });
-
-      elem.addEventListener('mousedown', (e) => {
-        if (e.button === 2) {
-          this.swapped = true;
-        }
-      });
-
-      elem.addEventListener('mouseup', (e) => {
-        if (e.button === 2) {
-          this.swapped = this.controlHeld;
-        }
-      });
-
-      this.initSketcher(elem);
-      this.mode = 'draw'; // Reset mode - it's confusing if left at 'fill'
-    }
-  };
-
-  /**
-   * Set up the sketcher instance
-   *
-   * @param canvas - canvas element. Null if we're just resizing
-   */
-  initSketcher (canvas = null) {
-    const sizepreset = DOODLE_SIZES[this.size];
-
-    if (this.sketcher) this.sketcher.destroy();
-    this.sketcher = new Atrament(canvas || this.canvas, sizepreset[0], sizepreset[1]);
-
-    if (canvas) {
-      this.ctx = this.sketcher.context;
-      this.updateSketcherSettings();
-    }
-
-    this.clearScreen();
-  }
-
-  /**
-   * Done button handler
-   */
-  onDoneButton = () => {
-    const dataUrl = this.sketcher.toImage();
-    const file = dataURLtoFile(dataUrl, 'doodle.png');
-    this.props.submit(file);
-    this.props.onClose(); // close dialog
-  };
-
-  /**
-   * Cancel button handler
-   */
-  onCancelButton = () => {
-    if (this.undos.length > 1 && !confirm('Discard doodle? All changes will be lost!')) {
-      return;
-    }
-
-    this.props.onClose(); // close dialog
-  };
-
-  /**
-   * Update sketcher options based on state
-   */
-  updateSketcherSettings () {
-    if (!this.sketcher) return;
-
-    if (this.oldSize !== this.size) this.initSketcher();
-
-    this.sketcher.color = (this.swapped ? this.bg : this.fg);
-    this.sketcher.opacity = this.opacity;
-    this.sketcher.weight = this.weight;
-    this.sketcher.mode = this.mode;
-    this.sketcher.smoothing = this.smoothing;
-    this.sketcher.adaptiveStroke = this.adaptiveStroke;
-
-    this.oldSize = this.size;
-  }
-
-  /**
-   * Fill screen with background color
-   */
-  clearScreen = () => {
-    this.ctx.fillStyle = this.bg;
-    this.ctx.fillRect(-1, -1, this.canvas.width+2, this.canvas.height+2);
-    this.undos = [];
-
-    this.doSaveUndo();
-  };
-
-  /**
-   * Undo one step
-   */
-  undo = () => {
-    if (this.undos.length > 1) {
-      this.undos.pop();
-      const buf = this.undos.pop();
-
-      this.sketcher.clear();
-      this.ctx.putImageData(buf, 0, 0);
-      this.doSaveUndo();
-    }
-  };
-
-  /**
-   * Save canvas content into the undo buffer immediately
-   */
-  doSaveUndo = () => {
-    this.undos.push(this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height));
-  };
-
-  /**
-   * Called on each canvas change.
-   * Saves canvas content to the undo buffer after some period of inactivity.
-   */
-  saveUndo = debounce(() => {
-    this.doSaveUndo();
-  }, 100);
-
-  /**
-   * Palette left click.
-   * Selects Fg color (or Bg, if Control/Meta is held)
-   *
-   * @param e - event
-   */
-  onPaletteClick = (e) => {
-    const c = e.target.dataset.color;
-
-    if (this.controlHeld) {
-      this.bg = c;
-    } else {
-      this.fg = c;
-    }
-
-    e.target.blur();
-    e.preventDefault();
-  };
-
-  /**
-   * Palette right click.
-   * Selects Bg color
-   *
-   * @param e - event
-   */
-  onPaletteRClick = (e) => {
-    this.bg = e.target.dataset.color;
-    e.target.blur();
-    e.preventDefault();
-  };
-
-  /**
-   * Handle click on the Draw mode button
-   *
-   * @param e - event
-   */
-  setModeDraw = (e) => {
-    this.mode = 'draw';
-    e.target.blur();
-  };
-
-  /**
-   * Handle click on the Fill mode button
-   *
-   * @param e - event
-   */
-  setModeFill = (e) => {
-    this.mode = 'fill';
-    e.target.blur();
-  };
-
-  /**
-   * Handle click on Smooth checkbox
-   *
-   * @param e - event
-   */
-  tglSmooth = (e) => {
-    this.smoothing = !this.smoothing;
-    e.target.blur();
-  };
-
-  /**
-   * Handle click on Adaptive checkbox
-   *
-   * @param e - event
-   */
-  tglAdaptive = (e) => {
-    this.adaptiveStroke = !this.adaptiveStroke;
-    e.target.blur();
-  };
-
-  /**
-   * Handle change of the Weight input field
-   *
-   * @param e - event
-   */
-  setWeight = (e) => {
-    this.weight = +e.target.value || 1;
-  };
-
-  /**
-   * Set size - clalback from the select box
-   *
-   * @param e - event
-   */
-  changeSize = (e) => {
-    let newSize = e.target.value;
-    if (newSize === this.oldSize) return;
-
-    if (this.undos.length > 1 && !confirm('Change size? This will erase your drawing!')) {
-      return;
-    }
-
-    this.size = newSize;
-  };
-
-  handleClearBtn = () => {
-    if (this.undos.length > 1 && !confirm('Clear screen? This will erase your drawing!')) {
-      return;
-    }
-
-    this.clearScreen();
-  };
-
-  /**
-   * Render the component
-   */
-  render () {
-    this.updateSketcherSettings();
-
-    return (
-      <div className='modal-root__modal doodle-modal'>
-        <div className='doodle-modal__container'>
-          <canvas ref={this.setCanvasRef} />
-        </div>
-
-        <div className='doodle-modal__action-bar'>
-          <div className='doodle-toolbar'>
-            <Button text='Done' onClick={this.onDoneButton} />
-            <Button text='Cancel' onClick={this.onCancelButton} />
-          </div>
-          <div className='filler' />
-          <div className='doodle-toolbar with-inputs'>
-            <div>
-              <label htmlFor='dd_smoothing'>Smoothing</label>
-              <span className='val'>
-                <input type='checkbox' id='dd_smoothing' onChange={this.tglSmooth} checked={this.smoothing} />
-              </span>
-            </div>
-            <div>
-              <label htmlFor='dd_adaptive'>Adaptive</label>
-              <span className='val'>
-                <input type='checkbox' id='dd_adaptive' onChange={this.tglAdaptive} checked={this.adaptiveStroke} />
-              </span>
-            </div>
-            <div>
-              <label htmlFor='dd_weight'>Weight</label>
-              <span className='val'>
-                <input type='number' min={1} id='dd_weight' value={this.weight} onChange={this.setWeight} />
-              </span>
-            </div>
-            <div>
-              <select aria-label='Canvas size' onInput={this.changeSize} defaultValue={this.size}>
-                { Object.values(mapValues(DOODLE_SIZES, (val, k) =>
-                  <option key={k} value={k}>{val[2]}</option>
-                )) }
-              </select>
-            </div>
-          </div>
-          <div className='doodle-toolbar'>
-            <IconButton icon='pencil' title='Draw' label='Draw' onClick={this.setModeDraw} size={18} active={this.mode === 'draw'} inverted />
-            <IconButton icon='bath' title='Fill' label='Fill' onClick={this.setModeFill} size={18} active={this.mode === 'fill'} inverted />
-            <IconButton icon='undo' title='Undo' label='Undo' onClick={this.undo} size={18} inverted />
-            <IconButton icon='trash' title='Clear' label='Clear' onClick={this.handleClearBtn} size={18} inverted />
-          </div>
-          <div className='doodle-palette'>
-            {
-              palReordered.map((c, i) =>
-                c === null ?
-                  <br key={i} /> :
-                  <button
-                    key={i}
-                    style={{ backgroundColor: c[0] }}
-                    onClick={this.onPaletteClick}
-                    onContextMenu={this.onPaletteRClick}
-                    data-color={c[0]}
-                    title={c[1]}
-                    className={classNames({
-                      'foreground': this.fg === c[0],
-                      'background': this.bg === c[0],
-                    })}
-                  />
-              )
-            }
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/drawer_loading.js b/app/javascript/themes/glitch/features/ui/components/drawer_loading.js
deleted file mode 100644
index 08b0d2347..000000000
--- a/app/javascript/themes/glitch/features/ui/components/drawer_loading.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from 'react';
-
-const DrawerLoading = () => (
-  <div className='drawer'>
-    <div className='drawer__pager'>
-      <div className='drawer__inner' />
-    </div>
-  </div>
-);
-
-export default DrawerLoading;
diff --git a/app/javascript/themes/glitch/features/ui/components/embed_modal.js b/app/javascript/themes/glitch/features/ui/components/embed_modal.js
deleted file mode 100644
index 1afffb51b..000000000
--- a/app/javascript/themes/glitch/features/ui/components/embed_modal.js
+++ /dev/null
@@ -1,84 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { FormattedMessage, injectIntl } from 'react-intl';
-import axios from 'axios';
-
-@injectIntl
-export default class EmbedModal extends ImmutablePureComponent {
-
-  static propTypes = {
-    url: PropTypes.string.isRequired,
-    onClose: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  }
-
-  state = {
-    loading: false,
-    oembed: null,
-  };
-
-  componentDidMount () {
-    const { url } = this.props;
-
-    this.setState({ loading: true });
-
-    axios.post('/api/web/embed', { url }).then(res => {
-      this.setState({ loading: false, oembed: res.data });
-
-      const iframeDocument = this.iframe.contentWindow.document;
-
-      iframeDocument.open();
-      iframeDocument.write(res.data.html);
-      iframeDocument.close();
-
-      iframeDocument.body.style.margin = 0;
-      this.iframe.width  = iframeDocument.body.scrollWidth;
-      this.iframe.height = iframeDocument.body.scrollHeight;
-    });
-  }
-
-  setIframeRef = c =>  {
-    this.iframe = c;
-  }
-
-  handleTextareaClick = (e) => {
-    e.target.select();
-  }
-
-  render () {
-    const { oembed } = this.state;
-
-    return (
-      <div className='modal-root__modal embed-modal'>
-        <h4><FormattedMessage id='status.embed' defaultMessage='Embed' /></h4>
-
-        <div className='embed-modal__container'>
-          <p className='hint'>
-            <FormattedMessage id='embed.instructions' defaultMessage='Embed this status on your website by copying the code below.' />
-          </p>
-
-          <input
-            type='text'
-            className='embed-modal__html'
-            readOnly
-            value={oembed && oembed.html || ''}
-            onClick={this.handleTextareaClick}
-          />
-
-          <p className='hint'>
-            <FormattedMessage id='embed.preview' defaultMessage='Here is what it will look like:' />
-          </p>
-
-          <iframe
-            className='embed-modal__iframe'
-            frameBorder='0'
-            ref={this.setIframeRef}
-            title='preview'
-          />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/image_loader.js b/app/javascript/themes/glitch/features/ui/components/image_loader.js
deleted file mode 100644
index aad594380..000000000
--- a/app/javascript/themes/glitch/features/ui/components/image_loader.js
+++ /dev/null
@@ -1,152 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-
-export default class ImageLoader extends React.PureComponent {
-
-  static propTypes = {
-    alt: PropTypes.string,
-    src: PropTypes.string.isRequired,
-    previewSrc: PropTypes.string.isRequired,
-    width: PropTypes.number,
-    height: PropTypes.number,
-  }
-
-  static defaultProps = {
-    alt: '',
-    width: null,
-    height: null,
-  };
-
-  state = {
-    loading: true,
-    error: false,
-  }
-
-  removers = [];
-
-  get canvasContext() {
-    if (!this.canvas) {
-      return null;
-    }
-    this._canvasContext = this._canvasContext || this.canvas.getContext('2d');
-    return this._canvasContext;
-  }
-
-  componentDidMount () {
-    this.loadImage(this.props);
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (this.props.src !== nextProps.src) {
-      this.loadImage(nextProps);
-    }
-  }
-
-  loadImage (props) {
-    this.removeEventListeners();
-    this.setState({ loading: true, error: false });
-    Promise.all([
-      this.loadPreviewCanvas(props),
-      this.hasSize() && this.loadOriginalImage(props),
-    ].filter(Boolean))
-      .then(() => {
-        this.setState({ loading: false, error: false });
-        this.clearPreviewCanvas();
-      })
-      .catch(() => this.setState({ loading: false, error: true }));
-  }
-
-  loadPreviewCanvas = ({ previewSrc, width, height }) => new Promise((resolve, reject) => {
-    const image = new Image();
-    const removeEventListeners = () => {
-      image.removeEventListener('error', handleError);
-      image.removeEventListener('load', handleLoad);
-    };
-    const handleError = () => {
-      removeEventListeners();
-      reject();
-    };
-    const handleLoad = () => {
-      removeEventListeners();
-      this.canvasContext.drawImage(image, 0, 0, width, height);
-      resolve();
-    };
-    image.addEventListener('error', handleError);
-    image.addEventListener('load', handleLoad);
-    image.src = previewSrc;
-    this.removers.push(removeEventListeners);
-  })
-
-  clearPreviewCanvas () {
-    const { width, height } = this.canvas;
-    this.canvasContext.clearRect(0, 0, width, height);
-  }
-
-  loadOriginalImage = ({ src }) => new Promise((resolve, reject) => {
-    const image = new Image();
-    const removeEventListeners = () => {
-      image.removeEventListener('error', handleError);
-      image.removeEventListener('load', handleLoad);
-    };
-    const handleError = () => {
-      removeEventListeners();
-      reject();
-    };
-    const handleLoad = () => {
-      removeEventListeners();
-      resolve();
-    };
-    image.addEventListener('error', handleError);
-    image.addEventListener('load', handleLoad);
-    image.src = src;
-    this.removers.push(removeEventListeners);
-  });
-
-  removeEventListeners () {
-    this.removers.forEach(listeners => listeners());
-    this.removers = [];
-  }
-
-  hasSize () {
-    const { width, height } = this.props;
-    return typeof width === 'number' && typeof height === 'number';
-  }
-
-  setCanvasRef = c => {
-    this.canvas = c;
-  }
-
-  render () {
-    const { alt, src, width, height } = this.props;
-    const { loading } = this.state;
-
-    const className = classNames('image-loader', {
-      'image-loader--loading': loading,
-      'image-loader--amorphous': !this.hasSize(),
-    });
-
-    return (
-      <div className={className}>
-        <canvas
-          className='image-loader__preview-canvas'
-          width={width}
-          height={height}
-          ref={this.setCanvasRef}
-          style={{ opacity: loading ? 1 : 0 }}
-        />
-
-        {!loading && (
-          <img
-            alt={alt}
-            className='image-loader__img'
-            src={src}
-            width={width}
-            height={height}
-          />
-        )}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/media_modal.js b/app/javascript/themes/glitch/features/ui/components/media_modal.js
deleted file mode 100644
index 1dad972b2..000000000
--- a/app/javascript/themes/glitch/features/ui/components/media_modal.js
+++ /dev/null
@@ -1,126 +0,0 @@
-import React from 'react';
-import ReactSwipeableViews from 'react-swipeable-views';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import ExtendedVideoPlayer from 'themes/glitch/components/extended_video_player';
-import { defineMessages, injectIntl } from 'react-intl';
-import IconButton from 'themes/glitch/components/icon_button';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import ImageLoader from './image_loader';
-
-const messages = defineMessages({
-  close: { id: 'lightbox.close', defaultMessage: 'Close' },
-  previous: { id: 'lightbox.previous', defaultMessage: 'Previous' },
-  next: { id: 'lightbox.next', defaultMessage: 'Next' },
-});
-
-@injectIntl
-export default class MediaModal extends ImmutablePureComponent {
-
-  static propTypes = {
-    media: ImmutablePropTypes.list.isRequired,
-    index: PropTypes.number.isRequired,
-    onClose: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  state = {
-    index: null,
-  };
-
-  handleSwipe = (index) => {
-    this.setState({ index: index % this.props.media.size });
-  }
-
-  handleNextClick = () => {
-    this.setState({ index: (this.getIndex() + 1) % this.props.media.size });
-  }
-
-  handlePrevClick = () => {
-    this.setState({ index: (this.props.media.size + this.getIndex() - 1) % this.props.media.size });
-  }
-
-  handleChangeIndex = (e) => {
-    const index = Number(e.currentTarget.getAttribute('data-index'));
-    this.setState({ index: index % this.props.media.size });
-  }
-
-  handleKeyUp = (e) => {
-    switch(e.key) {
-    case 'ArrowLeft':
-      this.handlePrevClick();
-      break;
-    case 'ArrowRight':
-      this.handleNextClick();
-      break;
-    }
-  }
-
-  componentDidMount () {
-    window.addEventListener('keyup', this.handleKeyUp, false);
-  }
-
-  componentWillUnmount () {
-    window.removeEventListener('keyup', this.handleKeyUp);
-  }
-
-  getIndex () {
-    return this.state.index !== null ? this.state.index : this.props.index;
-  }
-
-  render () {
-    const { media, intl, onClose } = this.props;
-
-    const index = this.getIndex();
-    let pagination = [];
-
-    const leftNav  = media.size > 1 && <button tabIndex='0' className='modal-container__nav modal-container__nav--left' onClick={this.handlePrevClick} aria-label={intl.formatMessage(messages.previous)}><i className='fa fa-fw fa-chevron-left' /></button>;
-    const rightNav = media.size > 1 && <button tabIndex='0' className='modal-container__nav  modal-container__nav--right' onClick={this.handleNextClick} aria-label={intl.formatMessage(messages.next)}><i className='fa fa-fw fa-chevron-right' /></button>;
-
-    if (media.size > 1) {
-      pagination = media.map((item, i) => {
-        const classes = ['media-modal__button'];
-        if (i === index) {
-          classes.push('media-modal__button--active');
-        }
-        return (<li className='media-modal__page-dot' key={i}><button tabIndex='0' className={classes.join(' ')} onClick={this.handleChangeIndex} data-index={i}>{i + 1}</button></li>);
-      });
-    }
-
-    const content = media.map((image) => {
-      const width  = image.getIn(['meta', 'original', 'width']) || null;
-      const height = image.getIn(['meta', 'original', 'height']) || null;
-
-      if (image.get('type') === 'image') {
-        return <ImageLoader previewSrc={image.get('preview_url')} src={image.get('url')} width={width} height={height} alt={image.get('description')} key={image.get('preview_url')} />;
-      } else if (image.get('type') === 'gifv') {
-        return <ExtendedVideoPlayer src={image.get('url')} muted controls={false} width={width} height={height} key={image.get('preview_url')} alt={image.get('description')} />;
-      }
-
-      return null;
-    }).toArray();
-
-    const containerStyle = {
-      alignItems: 'center', // center vertically
-    };
-
-    return (
-      <div className='modal-root__modal media-modal'>
-        {leftNav}
-
-        <div className='media-modal__content'>
-          <IconButton className='media-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={16} />
-          <ReactSwipeableViews containerStyle={containerStyle} onChangeIndex={this.handleSwipe} index={index}>
-            {content}
-          </ReactSwipeableViews>
-        </div>
-        <ul className='media-modal__pagination'>
-          {pagination}
-        </ul>
-
-        {rightNav}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/modal_loading.js b/app/javascript/themes/glitch/features/ui/components/modal_loading.js
deleted file mode 100644
index e14d20fbb..000000000
--- a/app/javascript/themes/glitch/features/ui/components/modal_loading.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import React from 'react';
-
-import LoadingIndicator from 'themes/glitch/components/loading_indicator';
-
-// Keep the markup in sync with <BundleModalError />
-// (make sure they have the same dimensions)
-const ModalLoading = () => (
-  <div className='modal-root__modal error-modal'>
-    <div className='error-modal__body'>
-      <LoadingIndicator />
-    </div>
-    <div className='error-modal__footer'>
-      <div>
-        <button className='error-modal__nav onboarding-modal__skip' />
-      </div>
-    </div>
-  </div>
-);
-
-export default ModalLoading;
diff --git a/app/javascript/themes/glitch/features/ui/components/modal_root.js b/app/javascript/themes/glitch/features/ui/components/modal_root.js
deleted file mode 100644
index fbe794170..000000000
--- a/app/javascript/themes/glitch/features/ui/components/modal_root.js
+++ /dev/null
@@ -1,131 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import BundleContainer from '../containers/bundle_container';
-import BundleModalError from './bundle_modal_error';
-import ModalLoading from './modal_loading';
-import ActionsModal from './actions_modal';
-import MediaModal from './media_modal';
-import VideoModal from './video_modal';
-import BoostModal from './boost_modal';
-import DoodleModal from './doodle_modal';
-import ConfirmationModal from './confirmation_modal';
-import {
-  OnboardingModal,
-  MuteModal,
-  ReportModal,
-  SettingsModal,
-  EmbedModal,
-} from 'themes/glitch/util/async-components';
-
-const MODAL_COMPONENTS = {
-  'MEDIA': () => Promise.resolve({ default: MediaModal }),
-  'ONBOARDING': OnboardingModal,
-  'VIDEO': () => Promise.resolve({ default: VideoModal }),
-  'BOOST': () => Promise.resolve({ default: BoostModal }),
-  'DOODLE': () => Promise.resolve({ default: DoodleModal }),
-  'CONFIRM': () => Promise.resolve({ default: ConfirmationModal }),
-  'MUTE': MuteModal,
-  'REPORT': ReportModal,
-  'SETTINGS': SettingsModal,
-  'ACTIONS': () => Promise.resolve({ default: ActionsModal }),
-  'EMBED': EmbedModal,
-};
-
-export default class ModalRoot extends React.PureComponent {
-
-  static propTypes = {
-    type: PropTypes.string,
-    props: PropTypes.object,
-    onClose: PropTypes.func.isRequired,
-  };
-
-  state = {
-    revealed: false,
-  };
-
-  handleKeyUp = (e) => {
-    if ((e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27)
-         && !!this.props.type && !this.props.props.noEsc) {
-      this.props.onClose();
-    }
-  }
-
-  componentDidMount () {
-    window.addEventListener('keyup', this.handleKeyUp, false);
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (!!nextProps.type && !this.props.type) {
-      this.activeElement = document.activeElement;
-
-      this.getSiblings().forEach(sibling => sibling.setAttribute('inert', true));
-    } else if (!nextProps.type) {
-      this.setState({ revealed: false });
-    }
-  }
-
-  componentDidUpdate (prevProps) {
-    if (!this.props.type && !!prevProps.type) {
-      this.getSiblings().forEach(sibling => sibling.removeAttribute('inert'));
-      this.activeElement.focus();
-      this.activeElement = null;
-    }
-    if (this.props.type) {
-      requestAnimationFrame(() => {
-        this.setState({ revealed: true });
-      });
-    }
-  }
-
-  componentWillUnmount () {
-    window.removeEventListener('keyup', this.handleKeyUp);
-  }
-
-  getSiblings = () => {
-    return Array(...this.node.parentElement.childNodes).filter(node => node !== this.node);
-  }
-
-  setRef = ref => {
-    this.node = ref;
-  }
-
-  renderLoading = modalId => () => {
-    return ['MEDIA', 'VIDEO', 'BOOST', 'DOODLE', 'CONFIRM', 'ACTIONS'].indexOf(modalId) === -1 ? <ModalLoading /> : null;
-  }
-
-  renderError = (props) => {
-    const { onClose } = this.props;
-
-    return <BundleModalError {...props} onClose={onClose} />;
-  }
-
-  render () {
-    const { type, props, onClose } = this.props;
-    const { revealed } = this.state;
-    const visible = !!type;
-
-    if (!visible) {
-      return (
-        <div className='modal-root' ref={this.setRef} style={{ opacity: 0 }} />
-      );
-    }
-
-    return (
-      <div className='modal-root' ref={this.setRef} style={{ opacity: revealed ? 1 : 0 }}>
-        <div style={{ pointerEvents: visible ? 'auto' : 'none' }}>
-          <div role='presentation' className='modal-root__overlay' onClick={onClose} />
-          <div role='dialog' className='modal-root__container'>
-            {
-              visible ?
-                (<BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}>
-                  {(SpecificComponent) => <SpecificComponent {...props} onClose={onClose} />}
-                </BundleContainer>) :
-              null
-            }
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/mute_modal.js b/app/javascript/themes/glitch/features/ui/components/mute_modal.js
deleted file mode 100644
index ffccdc84d..000000000
--- a/app/javascript/themes/glitch/features/ui/components/mute_modal.js
+++ /dev/null
@@ -1,105 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import { injectIntl, FormattedMessage } from 'react-intl';
-import Toggle from 'react-toggle';
-import Button from 'themes/glitch/components/button';
-import { closeModal } from 'themes/glitch/actions/modal';
-import { muteAccount } from 'themes/glitch/actions/accounts';
-import { toggleHideNotifications } from 'themes/glitch/actions/mutes';
-
-
-const mapStateToProps = state => {
-  return {
-    isSubmitting: state.getIn(['reports', 'new', 'isSubmitting']),
-    account: state.getIn(['mutes', 'new', 'account']),
-    notifications: state.getIn(['mutes', 'new', 'notifications']),
-  };
-};
-
-const mapDispatchToProps = dispatch => {
-  return {
-    onConfirm(account, notifications) {
-      dispatch(muteAccount(account.get('id'), notifications));
-    },
-
-    onClose() {
-      dispatch(closeModal());
-    },
-
-    onToggleNotifications() {
-      dispatch(toggleHideNotifications());
-    },
-  };
-};
-
-@connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
-export default class MuteModal extends React.PureComponent {
-
-  static propTypes = {
-    isSubmitting: PropTypes.bool.isRequired,
-    account: PropTypes.object.isRequired,
-    notifications: PropTypes.bool.isRequired,
-    onClose: PropTypes.func.isRequired,
-    onConfirm: PropTypes.func.isRequired,
-    onToggleNotifications: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  componentDidMount() {
-    this.button.focus();
-  }
-
-  handleClick = () => {
-    this.props.onClose();
-    this.props.onConfirm(this.props.account, this.props.notifications);
-  }
-
-  handleCancel = () => {
-    this.props.onClose();
-  }
-
-  setRef = (c) => {
-    this.button = c;
-  }
-
-  toggleNotifications = () => {
-    this.props.onToggleNotifications();
-  }
-
-  render () {
-    const { account, notifications } = this.props;
-
-    return (
-      <div className='modal-root__modal mute-modal'>
-        <div className='mute-modal__container'>
-          <p>
-            <FormattedMessage
-              id='confirmations.mute.message'
-              defaultMessage='Are you sure you want to mute {name}?'
-              values={{ name: <strong>@{account.get('acct')}</strong> }}
-            />
-          </p>
-          <div>
-            <label htmlFor='mute-modal__hide-notifications-checkbox'>
-              <FormattedMessage id='mute_modal.hide_notifications' defaultMessage='Hide notifications from this user?' />
-              {' '}
-              <Toggle id='mute-modal__hide-notifications-checkbox' checked={notifications} onChange={this.toggleNotifications} />
-            </label>
-          </div>
-        </div>
-
-        <div className='mute-modal__action-bar'>
-          <Button onClick={this.handleCancel} className='mute-modal__cancel-button'>
-            <FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' />
-          </Button>
-          <Button onClick={this.handleClick} ref={this.setRef}>
-            <FormattedMessage id='confirmations.mute.confirm' defaultMessage='Mute' />
-          </Button>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/onboarding_modal.js b/app/javascript/themes/glitch/features/ui/components/onboarding_modal.js
deleted file mode 100644
index 58875262e..000000000
--- a/app/javascript/themes/glitch/features/ui/components/onboarding_modal.js
+++ /dev/null
@@ -1,323 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import ReactSwipeableViews from 'react-swipeable-views';
-import classNames from 'classnames';
-import Permalink from 'themes/glitch/components/permalink';
-import ComposeForm from 'themes/glitch/features/compose/components/compose_form';
-import Search from 'themes/glitch/features/compose/components/search';
-import NavigationBar from 'themes/glitch/features/compose/components/navigation_bar';
-import ColumnHeader from './column_header';
-import {
-  List as ImmutableList,
-  Map as ImmutableMap,
-} from 'immutable';
-import { me } from 'themes/glitch/util/initial_state';
-
-const noop = () => { };
-
-const messages = defineMessages({
-  home_title: { id: 'column.home', defaultMessage: 'Home' },
-  notifications_title: { id: 'column.notifications', defaultMessage: 'Notifications' },
-  local_title: { id: 'column.community', defaultMessage: 'Local timeline' },
-  federated_title: { id: 'column.public', defaultMessage: 'Federated timeline' },
-});
-
-const PageOne = ({ acct, domain }) => (
-  <div className='onboarding-modal__page onboarding-modal__page-one'>
-    <div style={{ flex: '0 0 auto' }}>
-      <div className='onboarding-modal__page-one__elephant-friend' />
-    </div>
-
-    <div>
-      <h1><FormattedMessage id='onboarding.page_one.welcome' defaultMessage='Welcome to {domain}!' values={{ domain }} /></h1>
-      <p><FormattedMessage id='onboarding.page_one.federation' defaultMessage='{domain} is an "instance" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.' values={{ domain }} /></p>
-      <p><FormattedMessage id='onboarding.page_one.handle' defaultMessage='You are on {domain}, so your full handle is {handle}' values={{ domain, handle: <strong>@{acct}@{domain}</strong> }} /></p>
-    </div>
-  </div>
-);
-
-PageOne.propTypes = {
-  acct: PropTypes.string.isRequired,
-  domain: PropTypes.string.isRequired,
-};
-
-const PageTwo = ({ myAccount }) => (
-  <div className='onboarding-modal__page onboarding-modal__page-two'>
-    <div className='figure non-interactive'>
-      <div className='pseudo-drawer'>
-        <NavigationBar onClose={noop} account={myAccount} />
-      </div>
-      <ComposeForm
-        text='Awoo! #introductions'
-        suggestions={ImmutableList()}
-        mentionedDomains={[]}
-        spoiler={false}
-        onChange={noop}
-        onSubmit={noop}
-        onPaste={noop}
-        onPickEmoji={noop}
-        onChangeSpoilerText={noop}
-        onClearSuggestions={noop}
-        onFetchSuggestions={noop}
-        onSuggestionSelected={noop}
-        onPrivacyChange={noop}
-        showSearch
-        settings={ImmutableMap.of('side_arm', 'none')}
-      />
-    </div>
-
-    <p><FormattedMessage id='onboarding.page_two.compose' defaultMessage='Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.' /></p>
-  </div>
-);
-
-PageTwo.propTypes = {
-  myAccount: ImmutablePropTypes.map.isRequired,
-};
-
-const PageThree = ({ myAccount }) => (
-  <div className='onboarding-modal__page onboarding-modal__page-three'>
-    <div className='figure non-interactive'>
-      <Search
-        value=''
-        onChange={noop}
-        onSubmit={noop}
-        onClear={noop}
-        onShow={noop}
-      />
-
-      <div className='pseudo-drawer'>
-        <NavigationBar onClose={noop} account={myAccount} />
-      </div>
-    </div>
-
-    <p><FormattedMessage id='onboarding.page_three.search' defaultMessage='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.' values={{ illustration: <Permalink to='/timelines/tag/illustration' href='/tags/illustration'>#illustration</Permalink>, introductions: <Permalink to='/timelines/tag/introductions' href='/tags/introductions'>#introductions</Permalink> }} /></p>
-    <p><FormattedMessage id='onboarding.page_three.profile' defaultMessage='Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.' /></p>
-  </div>
-);
-
-PageThree.propTypes = {
-  myAccount: ImmutablePropTypes.map.isRequired,
-};
-
-const PageFour = ({ domain, intl }) => (
-  <div className='onboarding-modal__page onboarding-modal__page-four'>
-    <div className='onboarding-modal__page-four__columns'>
-      <div className='row'>
-        <div>
-          <div className='figure non-interactive'><ColumnHeader icon='home' type={intl.formatMessage(messages.home_title)} /></div>
-          <p><FormattedMessage id='onboarding.page_four.home' defaultMessage='The home timeline shows posts from people you follow.' /></p>
-        </div>
-
-        <div>
-          <div className='figure non-interactive'><ColumnHeader icon='bell' type={intl.formatMessage(messages.notifications_title)} /></div>
-          <p><FormattedMessage id='onboarding.page_four.notifications' defaultMessage='The notifications column shows when someone interacts with you.' /></p>
-        </div>
-      </div>
-
-      <div className='row'>
-        <div>
-          <div className='figure non-interactive' style={{ marginBottom: 0 }}><ColumnHeader icon='users' type={intl.formatMessage(messages.local_title)} /></div>
-        </div>
-
-        <div>
-          <div className='figure non-interactive' style={{ marginBottom: 0 }}><ColumnHeader icon='globe' type={intl.formatMessage(messages.federated_title)} /></div>
-        </div>
-      </div>
-
-      <p><FormattedMessage id='onboarding.page_five.public_timelines' defaultMessage='The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.' values={{ domain }} /></p>
-    </div>
-  </div>
-);
-
-PageFour.propTypes = {
-  domain: PropTypes.string.isRequired,
-  intl: PropTypes.object.isRequired,
-};
-
-const PageSix = ({ admin, domain }) => {
-  let adminSection = '';
-
-  if (admin) {
-    adminSection = (
-      <p>
-        <FormattedMessage id='onboarding.page_six.admin' defaultMessage="Your instance's admin is {admin}." values={{ admin: <Permalink href={admin.get('url')} to={`/accounts/${admin.get('id')}`}>@{admin.get('acct')}</Permalink> }} />
-        <br />
-        <FormattedMessage id='onboarding.page_six.read_guidelines' defaultMessage="Please read {domain}'s {guidelines}!" values={{ domain, guidelines: <a href='/about/more' target='_blank'><FormattedMessage id='onboarding.page_six.guidelines' defaultMessage='community guidelines' /></a> }} />
-      </p>
-    );
-  }
-
-  return (
-    <div className='onboarding-modal__page onboarding-modal__page-six'>
-      <h1><FormattedMessage id='onboarding.page_six.almost_done' defaultMessage='Almost done...' /></h1>
-      {adminSection}
-      <p><FormattedMessage id='onboarding.page_six.github' defaultMessage='{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.' values={{ domain, fork: <a href='https://en.wikipedia.org/wiki/Fork_(software_development)' target='_blank' rel='noopener'>fork</a>, Mastodon: <a href='https://github.com/tootsuite/mastodon' target='_blank' rel='noopener'>Mastodon</a>, github: <a href='https://github.com/glitch-soc/mastodon' target='_blank' rel='noopener'>GitHub</a> }} /></p>
-      <p><FormattedMessage id='onboarding.page_six.apps_available' defaultMessage='There are {apps} available for iOS, Android and other platforms.' values={{ domain, apps: <a href='https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md' target='_blank' rel='noopener'><FormattedMessage id='onboarding.page_six.various_app' defaultMessage='mobile apps' /></a> }} /></p>
-      <p><em><FormattedMessage id='onboarding.page_six.appetoot' defaultMessage='Bon Appetoot!' /></em></p>
-    </div>
-  );
-};
-
-PageSix.propTypes = {
-  admin: ImmutablePropTypes.map,
-  domain: PropTypes.string.isRequired,
-};
-
-const mapStateToProps = state => ({
-  myAccount: state.getIn(['accounts', me]),
-  admin: state.getIn(['accounts', state.getIn(['meta', 'admin'])]),
-  domain: state.getIn(['meta', 'domain']),
-});
-
-@connect(mapStateToProps)
-@injectIntl
-export default class OnboardingModal extends React.PureComponent {
-
-  static propTypes = {
-    onClose: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-    myAccount: ImmutablePropTypes.map.isRequired,
-    domain: PropTypes.string.isRequired,
-    admin: ImmutablePropTypes.map,
-  };
-
-  state = {
-    currentIndex: 0,
-  };
-
-  componentWillMount() {
-    const { myAccount, admin, domain, intl } = this.props;
-    this.pages = [
-      <PageOne acct={myAccount.get('acct')} domain={domain} />,
-      <PageTwo myAccount={myAccount} />,
-      <PageThree myAccount={myAccount} />,
-      <PageFour domain={domain} intl={intl} />,
-      <PageSix admin={admin} domain={domain} />,
-    ];
-  };
-
-  componentDidMount() {
-    window.addEventListener('keyup', this.handleKeyUp);
-  }
-
-  componentWillUnmount() {
-    window.addEventListener('keyup', this.handleKeyUp);
-  }
-
-  handleSkip = (e) => {
-    e.preventDefault();
-    this.props.onClose();
-  }
-
-  handleDot = (e) => {
-    const i = Number(e.currentTarget.getAttribute('data-index'));
-    e.preventDefault();
-    this.setState({ currentIndex: i });
-  }
-
-  handlePrev = () => {
-    this.setState(({ currentIndex }) => ({
-      currentIndex: Math.max(0, currentIndex - 1),
-    }));
-  }
-
-  handleNext = () => {
-    const { pages } = this;
-    this.setState(({ currentIndex }) => ({
-      currentIndex: Math.min(currentIndex + 1, pages.length - 1),
-    }));
-  }
-
-  handleSwipe = (index) => {
-    this.setState({ currentIndex: index });
-  }
-
-  handleKeyUp = ({ key }) => {
-    switch (key) {
-    case 'ArrowLeft':
-      this.handlePrev();
-      break;
-    case 'ArrowRight':
-      this.handleNext();
-      break;
-    }
-  }
-
-  handleClose = () => {
-    this.props.onClose();
-  }
-
-  render () {
-    const { pages } = this;
-    const { currentIndex } = this.state;
-    const hasMore = currentIndex < pages.length - 1;
-
-    const nextOrDoneBtn = hasMore ? (
-      <button
-        onClick={this.handleNext}
-        className='onboarding-modal__nav onboarding-modal__next'
-      >
-        <FormattedMessage id='onboarding.next' defaultMessage='Next' />
-      </button>
-    ) : (
-      <button
-        onClick={this.handleClose}
-        className='onboarding-modal__nav onboarding-modal__done'
-      >
-        <FormattedMessage id='onboarding.done' defaultMessage='Done' />
-      </button>
-    );
-
-    return (
-      <div className='modal-root__modal onboarding-modal'>
-        <ReactSwipeableViews index={currentIndex} onChangeIndex={this.handleSwipe} className='onboarding-modal__pager'>
-          {pages.map((page, i) => {
-            const className = classNames('onboarding-modal__page__wrapper', {
-              'onboarding-modal__page__wrapper--active': i === currentIndex,
-            });
-            return (
-              <div key={i} className={className}>{page}</div>
-            );
-          })}
-        </ReactSwipeableViews>
-
-        <div className='onboarding-modal__paginator'>
-          <div>
-            <button
-              onClick={this.handleSkip}
-              className='onboarding-modal__nav onboarding-modal__skip'
-            >
-              <FormattedMessage id='onboarding.skip' defaultMessage='Skip' />
-            </button>
-          </div>
-
-          <div className='onboarding-modal__dots'>
-            {pages.map((_, i) => {
-              const className = classNames('onboarding-modal__dot', {
-                active: i === currentIndex,
-              });
-              return (
-                <div
-                  key={`dot-${i}`}
-                  role='button'
-                  tabIndex='0'
-                  data-index={i}
-                  onClick={this.handleDot}
-                  className={className}
-                />
-              );
-            })}
-          </div>
-
-          <div>
-            {nextOrDoneBtn}
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/report_modal.js b/app/javascript/themes/glitch/features/ui/components/report_modal.js
deleted file mode 100644
index e6153948e..000000000
--- a/app/javascript/themes/glitch/features/ui/components/report_modal.js
+++ /dev/null
@@ -1,105 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import { changeReportComment, submitReport } from 'themes/glitch/actions/reports';
-import { refreshAccountTimeline } from 'themes/glitch/actions/timelines';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { makeGetAccount } from 'themes/glitch/selectors';
-import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
-import StatusCheckBox from 'themes/glitch/features/report/containers/status_check_box_container';
-import { OrderedSet } from 'immutable';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import Button from 'themes/glitch/components/button';
-
-const messages = defineMessages({
-  placeholder: { id: 'report.placeholder', defaultMessage: 'Additional comments' },
-  submit: { id: 'report.submit', defaultMessage: 'Submit' },
-});
-
-const makeMapStateToProps = () => {
-  const getAccount = makeGetAccount();
-
-  const mapStateToProps = state => {
-    const accountId = state.getIn(['reports', 'new', 'account_id']);
-
-    return {
-      isSubmitting: state.getIn(['reports', 'new', 'isSubmitting']),
-      account: getAccount(state, accountId),
-      comment: state.getIn(['reports', 'new', 'comment']),
-      statusIds: OrderedSet(state.getIn(['timelines', `account:${accountId}`, 'items'])).union(state.getIn(['reports', 'new', 'status_ids'])),
-    };
-  };
-
-  return mapStateToProps;
-};
-
-@connect(makeMapStateToProps)
-@injectIntl
-export default class ReportModal extends ImmutablePureComponent {
-
-  static propTypes = {
-    isSubmitting: PropTypes.bool,
-    account: ImmutablePropTypes.map,
-    statusIds: ImmutablePropTypes.orderedSet.isRequired,
-    comment: PropTypes.string.isRequired,
-    dispatch: PropTypes.func.isRequired,
-    intl: PropTypes.object.isRequired,
-  };
-
-  handleCommentChange = (e) => {
-    this.props.dispatch(changeReportComment(e.target.value));
-  }
-
-  handleSubmit = () => {
-    this.props.dispatch(submitReport());
-  }
-
-  componentDidMount () {
-    this.props.dispatch(refreshAccountTimeline(this.props.account.get('id')));
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (this.props.account !== nextProps.account && nextProps.account) {
-      this.props.dispatch(refreshAccountTimeline(nextProps.account.get('id')));
-    }
-  }
-
-  render () {
-    const { account, comment, intl, statusIds, isSubmitting } = this.props;
-
-    if (!account) {
-      return null;
-    }
-
-    return (
-      <div className='modal-root__modal report-modal'>
-        <div className='report-modal__target'>
-          <FormattedMessage id='report.target' defaultMessage='Report {target}' values={{ target: <strong>{account.get('acct')}</strong> }} />
-        </div>
-
-        <div className='report-modal__container'>
-          <div className='report-modal__statuses'>
-            <div>
-              {statusIds.map(statusId => <StatusCheckBox id={statusId} key={statusId} disabled={isSubmitting} />)}
-            </div>
-          </div>
-
-          <div className='report-modal__comment'>
-            <textarea
-              className='setting-text light'
-              placeholder={intl.formatMessage(messages.placeholder)}
-              value={comment}
-              onChange={this.handleCommentChange}
-              disabled={isSubmitting}
-            />
-          </div>
-        </div>
-
-        <div className='report-modal__action-bar'>
-          <Button disabled={isSubmitting} text={intl.formatMessage(messages.submit)} onClick={this.handleSubmit} />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/tabs_bar.js b/app/javascript/themes/glitch/features/ui/components/tabs_bar.js
deleted file mode 100644
index ef5deae99..000000000
--- a/app/javascript/themes/glitch/features/ui/components/tabs_bar.js
+++ /dev/null
@@ -1,84 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { NavLink } from 'react-router-dom';
-import { FormattedMessage, injectIntl } from 'react-intl';
-import { debounce } from 'lodash';
-import { isUserTouching } from 'themes/glitch/util/is_mobile';
-
-export const links = [
-  <NavLink className='tabs-bar__link primary' to='/statuses/new' data-preview-title-id='tabs_bar.compose' data-preview-icon='pencil' ><i className='fa fa-fw fa-pencil' /><FormattedMessage id='tabs_bar.compose' defaultMessage='Compose' /></NavLink>,
-  <NavLink className='tabs-bar__link primary' to='/timelines/home' data-preview-title-id='column.home' data-preview-icon='home' ><i className='fa fa-fw fa-home' /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink>,
-  <NavLink className='tabs-bar__link primary' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><i className='fa fa-fw fa-bell' /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>,
-
-  <NavLink className='tabs-bar__link secondary' to='/timelines/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><i className='fa fa-fw fa-users' /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink>,
-  <NavLink className='tabs-bar__link secondary' exact to='/timelines/public' data-preview-title-id='column.public' data-preview-icon='globe' ><i className='fa fa-fw fa-globe' /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink>,
-
-  <NavLink className='tabs-bar__link primary' style={{ flexGrow: '0', flexBasis: '30px' }} to='/getting-started' data-preview-title-id='getting_started.heading' data-preview-icon='asterisk' ><i className='fa fa-fw fa-asterisk' /></NavLink>,
-];
-
-export function getIndex (path) {
-  return links.findIndex(link => link.props.to === path);
-}
-
-export function getLink (index) {
-  return links[index].props.to;
-}
-
-@injectIntl
-export default class TabsBar extends React.Component {
-
-  static contextTypes = {
-    router: PropTypes.object.isRequired,
-  }
-
-  static propTypes = {
-    intl: PropTypes.object.isRequired,
-  }
-
-  setRef = ref => {
-    this.node = ref;
-  }
-
-  handleClick = (e) => {
-    // Only apply optimization for touch devices, which we assume are slower
-    // We thus avoid the 250ms delay for non-touch devices and the lag for touch devices
-    if (isUserTouching()) {
-      e.preventDefault();
-      e.persist();
-
-      requestAnimationFrame(() => {
-        const tabs = Array(...this.node.querySelectorAll('.tabs-bar__link'));
-        const currentTab = tabs.find(tab => tab.classList.contains('active'));
-        const nextTab = tabs.find(tab => tab.contains(e.target));
-        const { props: { to } } = links[Array(...this.node.childNodes).indexOf(nextTab)];
-
-
-        if (currentTab !== nextTab) {
-          if (currentTab) {
-            currentTab.classList.remove('active');
-          }
-
-          const listener = debounce(() => {
-            nextTab.removeEventListener('transitionend', listener);
-            this.context.router.history.push(to);
-          }, 50);
-
-          nextTab.addEventListener('transitionend', listener);
-          nextTab.classList.add('active');
-        }
-      });
-    }
-
-  }
-
-  render () {
-    const { intl: { formatMessage } } = this.props;
-
-    return (
-      <nav className='tabs-bar' ref={this.setRef}>
-        {links.map(link => React.cloneElement(link, { key: link.props.to, onClick: this.handleClick, 'aria-label': formatMessage({ id: link.props['data-preview-title-id'] }) }))}
-      </nav>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/upload_area.js b/app/javascript/themes/glitch/features/ui/components/upload_area.js
deleted file mode 100644
index 72a450215..000000000
--- a/app/javascript/themes/glitch/features/ui/components/upload_area.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Motion from 'themes/glitch/util/optional_motion';
-import spring from 'react-motion/lib/spring';
-import { FormattedMessage } from 'react-intl';
-
-export default class UploadArea extends React.PureComponent {
-
-  static propTypes = {
-    active: PropTypes.bool,
-    onClose: PropTypes.func,
-  };
-
-  handleKeyUp = (e) => {
-    const keyCode = e.keyCode;
-    if (this.props.active) {
-      switch(keyCode) {
-      case 27:
-        e.preventDefault();
-        e.stopPropagation();
-        this.props.onClose();
-        break;
-      }
-    }
-  }
-
-  componentDidMount () {
-    window.addEventListener('keyup', this.handleKeyUp, false);
-  }
-
-  componentWillUnmount () {
-    window.removeEventListener('keyup', this.handleKeyUp);
-  }
-
-  render () {
-    const { active } = this.props;
-
-    return (
-      <Motion defaultStyle={{ backgroundOpacity: 0, backgroundScale: 0.95 }} style={{ backgroundOpacity: spring(active ? 1 : 0, { stiffness: 150, damping: 15 }), backgroundScale: spring(active ? 1 : 0.95, { stiffness: 200, damping: 3 }) }}>
-        {({ backgroundOpacity, backgroundScale }) =>
-          <div className='upload-area' style={{ visibility: active ? 'visible' : 'hidden', opacity: backgroundOpacity }}>
-            <div className='upload-area__drop'>
-              <div className='upload-area__background' style={{ transform: `scale(${backgroundScale})` }} />
-              <div className='upload-area__content'><FormattedMessage id='upload_area.title' defaultMessage='Drag & drop to upload' /></div>
-            </div>
-          </div>
-        }
-      </Motion>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/components/video_modal.js b/app/javascript/themes/glitch/features/ui/components/video_modal.js
deleted file mode 100644
index 91168c790..000000000
--- a/app/javascript/themes/glitch/features/ui/components/video_modal.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import Video from 'themes/glitch/features/video';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-export default class VideoModal extends ImmutablePureComponent {
-
-  static propTypes = {
-    media: ImmutablePropTypes.map.isRequired,
-    time: PropTypes.number,
-    onClose: PropTypes.func.isRequired,
-  };
-
-  render () {
-    const { media, time, onClose } = this.props;
-
-    return (
-      <div className='modal-root__modal media-modal'>
-        <div>
-          <Video
-            preview={media.get('preview_url')}
-            src={media.get('url')}
-            startTime={time}
-            onCloseVideo={onClose}
-            description={media.get('description')}
-          />
-        </div>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/ui/containers/bundle_container.js b/app/javascript/themes/glitch/features/ui/containers/bundle_container.js
deleted file mode 100644
index e6f9afcf7..000000000
--- a/app/javascript/themes/glitch/features/ui/containers/bundle_container.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import { connect } from 'react-redux';
-
-import Bundle from '../components/bundle';
-
-import { fetchBundleRequest, fetchBundleSuccess, fetchBundleFail } from 'themes/glitch/actions/bundles';
-
-const mapDispatchToProps = dispatch => ({
-  onFetch () {
-    dispatch(fetchBundleRequest());
-  },
-  onFetchSuccess () {
-    dispatch(fetchBundleSuccess());
-  },
-  onFetchFail (error) {
-    dispatch(fetchBundleFail(error));
-  },
-});
-
-export default connect(null, mapDispatchToProps)(Bundle);
diff --git a/app/javascript/themes/glitch/features/ui/containers/columns_area_container.js b/app/javascript/themes/glitch/features/ui/containers/columns_area_container.js
deleted file mode 100644
index 95f95618b..000000000
--- a/app/javascript/themes/glitch/features/ui/containers/columns_area_container.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { connect } from 'react-redux';
-import ColumnsArea from '../components/columns_area';
-
-const mapStateToProps = state => ({
-  columns: state.getIn(['settings', 'columns']),
-});
-
-export default connect(mapStateToProps, null, null, { withRef: true })(ColumnsArea);
diff --git a/app/javascript/themes/glitch/features/ui/containers/loading_bar_container.js b/app/javascript/themes/glitch/features/ui/containers/loading_bar_container.js
deleted file mode 100644
index 4bb90fb68..000000000
--- a/app/javascript/themes/glitch/features/ui/containers/loading_bar_container.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { connect }    from 'react-redux';
-import LoadingBar from 'react-redux-loading-bar';
-
-const mapStateToProps = (state) => ({
-  loading: state.get('loadingBar'),
-});
-
-export default connect(mapStateToProps)(LoadingBar.WrappedComponent);
diff --git a/app/javascript/themes/glitch/features/ui/containers/modal_container.js b/app/javascript/themes/glitch/features/ui/containers/modal_container.js
deleted file mode 100644
index c26f19886..000000000
--- a/app/javascript/themes/glitch/features/ui/containers/modal_container.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { connect } from 'react-redux';
-import { closeModal } from 'themes/glitch/actions/modal';
-import ModalRoot from '../components/modal_root';
-
-const mapStateToProps = state => ({
-  type: state.get('modal').modalType,
-  props: state.get('modal').modalProps,
-});
-
-const mapDispatchToProps = dispatch => ({
-  onClose () {
-    dispatch(closeModal());
-  },
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(ModalRoot);
diff --git a/app/javascript/themes/glitch/features/ui/containers/notifications_container.js b/app/javascript/themes/glitch/features/ui/containers/notifications_container.js
deleted file mode 100644
index 5bd4017f5..000000000
--- a/app/javascript/themes/glitch/features/ui/containers/notifications_container.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { connect } from 'react-redux';
-import { NotificationStack } from 'react-notification';
-import { dismissAlert } from 'themes/glitch/actions/alerts';
-import { getAlerts } from 'themes/glitch/selectors';
-
-const mapStateToProps = state => ({
-  notifications: getAlerts(state),
-});
-
-const mapDispatchToProps = (dispatch) => {
-  return {
-    onDismiss: alert => {
-      dispatch(dismissAlert(alert));
-    },
-  };
-};
-
-export default connect(mapStateToProps, mapDispatchToProps)(NotificationStack);
diff --git a/app/javascript/themes/glitch/features/ui/containers/status_list_container.js b/app/javascript/themes/glitch/features/ui/containers/status_list_container.js
deleted file mode 100644
index 807c82e16..000000000
--- a/app/javascript/themes/glitch/features/ui/containers/status_list_container.js
+++ /dev/null
@@ -1,73 +0,0 @@
-import { connect } from 'react-redux';
-import StatusList from 'themes/glitch/components/status_list';
-import { scrollTopTimeline } from 'themes/glitch/actions/timelines';
-import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
-import { createSelector } from 'reselect';
-import { debounce } from 'lodash';
-import { me } from 'themes/glitch/util/initial_state';
-
-const makeGetStatusIds = () => createSelector([
-  (state, { type }) => state.getIn(['settings', type], ImmutableMap()),
-  (state, { type }) => state.getIn(['timelines', type, 'items'], ImmutableList()),
-  (state)           => state.get('statuses'),
-], (columnSettings, statusIds, statuses) => {
-  const rawRegex = columnSettings.getIn(['regex', 'body'], '').trim();
-  let regex      = null;
-
-  try {
-    regex = rawRegex && new RegExp(rawRegex, 'i');
-  } catch (e) {
-    // Bad regex, don't affect filters
-  }
-
-  return statusIds.filter(id => {
-    const statusForId = statuses.get(id);
-    let showStatus    = true;
-
-    if (columnSettings.getIn(['shows', 'reblog']) === false) {
-      showStatus = showStatus && statusForId.get('reblog') === null;
-    }
-
-    if (columnSettings.getIn(['shows', 'reply']) === false) {
-      showStatus = showStatus && (statusForId.get('in_reply_to_id') === null || statusForId.get('in_reply_to_account_id') === me);
-    }
-
-    if (showStatus && regex && statusForId.get('account') !== me) {
-      const searchIndex = statusForId.get('reblog') ? statuses.getIn([statusForId.get('reblog'), 'search_index']) : statusForId.get('search_index');
-      showStatus = !regex.test(searchIndex);
-    }
-
-    return showStatus;
-  });
-});
-
-const makeMapStateToProps = () => {
-  const getStatusIds = makeGetStatusIds();
-
-  const mapStateToProps = (state, { timelineId }) => ({
-    statusIds: getStatusIds(state, { type: timelineId }),
-    isLoading: state.getIn(['timelines', timelineId, 'isLoading'], true),
-    hasMore: !!state.getIn(['timelines', timelineId, 'next']),
-  });
-
-  return mapStateToProps;
-};
-
-const mapDispatchToProps = (dispatch, { timelineId, loadMore }) => ({
-
-  onScrollToBottom: debounce(() => {
-    dispatch(scrollTopTimeline(timelineId, false));
-    loadMore();
-  }, 300, { leading: true }),
-
-  onScrollToTop: debounce(() => {
-    dispatch(scrollTopTimeline(timelineId, true));
-  }, 100),
-
-  onScroll: debounce(() => {
-    dispatch(scrollTopTimeline(timelineId, false));
-  }, 100),
-
-});
-
-export default connect(makeMapStateToProps, mapDispatchToProps)(StatusList);
diff --git a/app/javascript/themes/glitch/features/ui/index.js b/app/javascript/themes/glitch/features/ui/index.js
deleted file mode 100644
index 3eea63189..000000000
--- a/app/javascript/themes/glitch/features/ui/index.js
+++ /dev/null
@@ -1,443 +0,0 @@
-import React from 'react';
-import NotificationsContainer from './containers/notifications_container';
-import PropTypes from 'prop-types';
-import LoadingBarContainer from './containers/loading_bar_container';
-import TabsBar from './components/tabs_bar';
-import ModalContainer from './containers/modal_container';
-import { connect } from 'react-redux';
-import { Redirect, withRouter } from 'react-router-dom';
-import { isMobile } from 'themes/glitch/util/is_mobile';
-import { debounce } from 'lodash';
-import { uploadCompose, resetCompose } from 'themes/glitch/actions/compose';
-import { refreshHomeTimeline } from 'themes/glitch/actions/timelines';
-import { refreshNotifications } from 'themes/glitch/actions/notifications';
-import { clearHeight } from 'themes/glitch/actions/height_cache';
-import { WrappedSwitch, WrappedRoute } from 'themes/glitch/util/react_router_helpers';
-import UploadArea from './components/upload_area';
-import ColumnsAreaContainer from './containers/columns_area_container';
-import classNames from 'classnames';
-import {
-  Compose,
-  Status,
-  GettingStarted,
-  PublicTimeline,
-  CommunityTimeline,
-  AccountTimeline,
-  AccountGallery,
-  HomeTimeline,
-  Followers,
-  Following,
-  Reblogs,
-  Favourites,
-  DirectTimeline,
-  HashtagTimeline,
-  Notifications,
-  FollowRequests,
-  GenericNotFound,
-  FavouritedStatuses,
-  Blocks,
-  Mutes,
-  PinnedStatuses,
-} from 'themes/glitch/util/async-components';
-import { HotKeys } from 'react-hotkeys';
-import { me } from 'themes/glitch/util/initial_state';
-import { defineMessages, injectIntl } from 'react-intl';
-
-// Dummy import, to make sure that <Status /> ends up in the application bundle.
-// Without this it ends up in ~8 very commonly used bundles.
-import '../../../glitch/components/status';
-
-const messages = defineMessages({
-  beforeUnload: { id: 'ui.beforeunload', defaultMessage: 'Your draft will be lost if you leave Mastodon.' },
-});
-
-const mapStateToProps = state => ({
-  isComposing: state.getIn(['compose', 'is_composing']),
-  hasComposingText: state.getIn(['compose', 'text']) !== '',
-  layout: state.getIn(['local_settings', 'layout']),
-  isWide: state.getIn(['local_settings', 'stretch']),
-  navbarUnder: state.getIn(['local_settings', 'navbar_under']),
-});
-
-const keyMap = {
-  new: 'n',
-  search: 's',
-  forceNew: 'option+n',
-  focusColumn: ['1', '2', '3', '4', '5', '6', '7', '8', '9'],
-  reply: 'r',
-  favourite: 'f',
-  boost: 'b',
-  mention: 'm',
-  open: ['enter', 'o'],
-  openProfile: 'p',
-  moveDown: ['down', 'j'],
-  moveUp: ['up', 'k'],
-  back: 'backspace',
-  goToHome: 'g h',
-  goToNotifications: 'g n',
-  goToLocal: 'g l',
-  goToFederated: 'g t',
-  goToDirect: 'g d',
-  goToStart: 'g s',
-  goToFavourites: 'g f',
-  goToPinned: 'g p',
-  goToProfile: 'g u',
-  goToBlocked: 'g b',
-  goToMuted: 'g m',
-  toggleSpoiler: 'x',
-};
-
-@connect(mapStateToProps)
-@injectIntl
-@withRouter
-export default class UI extends React.Component {
-
-  static contextTypes = {
-    router: PropTypes.object.isRequired,
-  };
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    children: PropTypes.node,
-    layout: PropTypes.string,
-    isWide: PropTypes.bool,
-    systemFontUi: PropTypes.bool,
-    navbarUnder: PropTypes.bool,
-    isComposing: PropTypes.bool,
-    hasComposingText: PropTypes.bool,
-    location: PropTypes.object,
-    intl: PropTypes.object.isRequired,
-  };
-
-  state = {
-    width: window.innerWidth,
-    draggingOver: false,
-  };
-
-  handleBeforeUnload = (e) => {
-    const { intl, isComposing, hasComposingText } = this.props;
-
-    if (isComposing && hasComposingText) {
-      // Setting returnValue to any string causes confirmation dialog.
-      // Many browsers no longer display this text to users,
-      // but we set user-friendly message for other browsers, e.g. Edge.
-      e.returnValue = intl.formatMessage(messages.beforeUnload);
-    }
-  }
-
-  handleResize = debounce(() => {
-    // The cached heights are no longer accurate, invalidate
-    this.props.dispatch(clearHeight());
-
-    this.setState({ width: window.innerWidth });
-  }, 500, {
-    trailing: true,
-  });
-
-  handleDragEnter = (e) => {
-    e.preventDefault();
-
-    if (!this.dragTargets) {
-      this.dragTargets = [];
-    }
-
-    if (this.dragTargets.indexOf(e.target) === -1) {
-      this.dragTargets.push(e.target);
-    }
-
-    if (e.dataTransfer && e.dataTransfer.types.includes('Files')) {
-      this.setState({ draggingOver: true });
-    }
-  }
-
-  handleDragOver = (e) => {
-    e.preventDefault();
-    e.stopPropagation();
-
-    try {
-      e.dataTransfer.dropEffect = 'copy';
-    } catch (err) {
-
-    }
-
-    return false;
-  }
-
-  handleDrop = (e) => {
-    e.preventDefault();
-
-    this.setState({ draggingOver: false });
-
-    if (e.dataTransfer && e.dataTransfer.files.length === 1) {
-      this.props.dispatch(uploadCompose(e.dataTransfer.files));
-    }
-  }
-
-  handleDragLeave = (e) => {
-    e.preventDefault();
-    e.stopPropagation();
-
-    this.dragTargets = this.dragTargets.filter(el => el !== e.target && this.node.contains(el));
-
-    if (this.dragTargets.length > 0) {
-      return;
-    }
-
-    this.setState({ draggingOver: false });
-  }
-
-  closeUploadModal = () => {
-    this.setState({ draggingOver: false });
-  }
-
-  handleServiceWorkerPostMessage = ({ data }) => {
-    if (data.type === 'navigate') {
-      this.context.router.history.push(data.path);
-    } else {
-      console.warn('Unknown message type:', data.type);
-    }
-  }
-
-  componentWillMount () {
-    window.addEventListener('beforeunload', this.handleBeforeUnload, false);
-    window.addEventListener('resize', this.handleResize, { passive: true });
-    document.addEventListener('dragenter', this.handleDragEnter, false);
-    document.addEventListener('dragover', this.handleDragOver, false);
-    document.addEventListener('drop', this.handleDrop, false);
-    document.addEventListener('dragleave', this.handleDragLeave, false);
-    document.addEventListener('dragend', this.handleDragEnd, false);
-
-    if ('serviceWorker' in  navigator) {
-      navigator.serviceWorker.addEventListener('message', this.handleServiceWorkerPostMessage);
-    }
-
-    this.props.dispatch(refreshHomeTimeline());
-    this.props.dispatch(refreshNotifications());
-  }
-
-  componentDidMount () {
-    this.hotkeys.__mousetrap__.stopCallback = (e, element) => {
-      return ['TEXTAREA', 'SELECT', 'INPUT'].includes(element.tagName);
-    };
-  }
-
-  shouldComponentUpdate (nextProps) {
-    if (nextProps.isComposing !== this.props.isComposing) {
-      // Avoid expensive update just to toggle a class
-      this.node.classList.toggle('is-composing', nextProps.isComposing);
-      this.node.classList.toggle('navbar-under', nextProps.navbarUnder);
-
-      return false;
-    }
-
-    // Why isn't this working?!?
-    // return super.shouldComponentUpdate(nextProps, nextState);
-    return true;
-  }
-
-  componentDidUpdate (prevProps) {
-    if (![this.props.location.pathname, '/'].includes(prevProps.location.pathname)) {
-      this.columnsAreaNode.handleChildrenContentChange();
-    }
-  }
-
-  componentWillUnmount () {
-    window.removeEventListener('beforeunload', this.handleBeforeUnload);
-    window.removeEventListener('resize', this.handleResize);
-    document.removeEventListener('dragenter', this.handleDragEnter);
-    document.removeEventListener('dragover', this.handleDragOver);
-    document.removeEventListener('drop', this.handleDrop);
-    document.removeEventListener('dragleave', this.handleDragLeave);
-    document.removeEventListener('dragend', this.handleDragEnd);
-  }
-
-  setRef = c => {
-    this.node = c;
-  }
-
-  setColumnsAreaRef = c => {
-    this.columnsAreaNode = c.getWrappedInstance().getWrappedInstance();
-  }
-
-  handleHotkeyNew = e => {
-    e.preventDefault();
-
-    const element = this.node.querySelector('.compose-form__autosuggest-wrapper textarea');
-
-    if (element) {
-      element.focus();
-    }
-  }
-
-  handleHotkeySearch = e => {
-    e.preventDefault();
-
-    const element = this.node.querySelector('.search__input');
-
-    if (element) {
-      element.focus();
-    }
-  }
-
-  handleHotkeyForceNew = e => {
-    this.handleHotkeyNew(e);
-    this.props.dispatch(resetCompose());
-  }
-
-  handleHotkeyFocusColumn = e => {
-    const index  = (e.key * 1) + 1; // First child is drawer, skip that
-    const column = this.node.querySelector(`.column:nth-child(${index})`);
-
-    if (column) {
-      const status = column.querySelector('.focusable');
-
-      if (status) {
-        status.focus();
-      }
-    }
-  }
-
-  handleHotkeyBack = () => {
-    if (window.history && window.history.length === 1) {
-      this.context.router.history.push('/');
-    } else {
-      this.context.router.history.goBack();
-    }
-  }
-
-  setHotkeysRef = c => {
-    this.hotkeys = c;
-  }
-
-  handleHotkeyGoToHome = () => {
-    this.context.router.history.push('/timelines/home');
-  }
-
-  handleHotkeyGoToNotifications = () => {
-    this.context.router.history.push('/notifications');
-  }
-
-  handleHotkeyGoToLocal = () => {
-    this.context.router.history.push('/timelines/public/local');
-  }
-
-  handleHotkeyGoToFederated = () => {
-    this.context.router.history.push('/timelines/public');
-  }
-
-  handleHotkeyGoToDirect = () => {
-    this.context.router.history.push('/timelines/direct');
-  }
-
-  handleHotkeyGoToStart = () => {
-    this.context.router.history.push('/getting-started');
-  }
-
-  handleHotkeyGoToFavourites = () => {
-    this.context.router.history.push('/favourites');
-  }
-
-  handleHotkeyGoToPinned = () => {
-    this.context.router.history.push('/pinned');
-  }
-
-  handleHotkeyGoToProfile = () => {
-    this.context.router.history.push(`/accounts/${me}`);
-  }
-
-  handleHotkeyGoToBlocked = () => {
-    this.context.router.history.push('/blocks');
-  }
-
-  handleHotkeyGoToMuted = () => {
-    this.context.router.history.push('/mutes');
-  }
-
-  render () {
-    const { width, draggingOver } = this.state;
-    const { children, layout, isWide, navbarUnder } = this.props;
-
-    const columnsClass = layout => {
-      switch (layout) {
-      case 'single':
-        return 'single-column';
-      case 'multiple':
-        return 'multi-columns';
-      default:
-        return 'auto-columns';
-      }
-    };
-
-    const className = classNames('ui', columnsClass(layout), {
-      'wide': isWide,
-      'system-font': this.props.systemFontUi,
-      'navbar-under': navbarUnder,
-    });
-
-    const handlers = {
-      new: this.handleHotkeyNew,
-      search: this.handleHotkeySearch,
-      forceNew: this.handleHotkeyForceNew,
-      focusColumn: this.handleHotkeyFocusColumn,
-      back: this.handleHotkeyBack,
-      goToHome: this.handleHotkeyGoToHome,
-      goToNotifications: this.handleHotkeyGoToNotifications,
-      goToLocal: this.handleHotkeyGoToLocal,
-      goToFederated: this.handleHotkeyGoToFederated,
-      goToDirect: this.handleHotkeyGoToDirect,
-      goToStart: this.handleHotkeyGoToStart,
-      goToFavourites: this.handleHotkeyGoToFavourites,
-      goToPinned: this.handleHotkeyGoToPinned,
-      goToProfile: this.handleHotkeyGoToProfile,
-      goToBlocked: this.handleHotkeyGoToBlocked,
-      goToMuted: this.handleHotkeyGoToMuted,
-    };
-
-    return (
-      <HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef}>
-        <div className={className} ref={this.setRef}>
-          {navbarUnder ? null : (<TabsBar />)}
-
-          <ColumnsAreaContainer ref={this.setColumnsAreaRef} singleColumn={isMobile(width, layout)}>
-            <WrappedSwitch>
-              <Redirect from='/' to='/getting-started' exact />
-              <WrappedRoute path='/getting-started' component={GettingStarted} content={children} />
-              <WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} />
-              <WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} />
-              <WrappedRoute path='/timelines/public/local' component={CommunityTimeline} content={children} />
-              <WrappedRoute path='/timelines/direct' component={DirectTimeline} content={children} />
-              <WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} />
-
-              <WrappedRoute path='/notifications' component={Notifications} content={children} />
-              <WrappedRoute path='/favourites' component={FavouritedStatuses} content={children} />
-              <WrappedRoute path='/pinned' component={PinnedStatuses} content={children} />
-
-              <WrappedRoute path='/statuses/new' component={Compose} content={children} />
-              <WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} />
-              <WrappedRoute path='/statuses/:statusId/reblogs' component={Reblogs} content={children} />
-              <WrappedRoute path='/statuses/:statusId/favourites' component={Favourites} content={children} />
-
-              <WrappedRoute path='/accounts/:accountId' exact component={AccountTimeline} content={children} />
-              <WrappedRoute path='/accounts/:accountId/followers' component={Followers} content={children} />
-              <WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} />
-              <WrappedRoute path='/accounts/:accountId/media' component={AccountGallery} content={children} />
-
-              <WrappedRoute path='/follow_requests' component={FollowRequests} content={children} />
-              <WrappedRoute path='/blocks' component={Blocks} content={children} />
-              <WrappedRoute path='/mutes' component={Mutes} content={children} />
-
-              <WrappedRoute component={GenericNotFound} content={children} />
-            </WrappedSwitch>
-          </ColumnsAreaContainer>
-
-          <NotificationsContainer />
-          {navbarUnder ? (<TabsBar />) : null}
-          <LoadingBarContainer className='loading-bar' />
-          <ModalContainer />
-          <UploadArea active={draggingOver} onClose={this.closeUploadModal} />
-        </div>
-      </HotKeys>
-    );
-  }
-
-}
diff --git a/app/javascript/themes/glitch/features/video/index.js b/app/javascript/themes/glitch/features/video/index.js
deleted file mode 100644
index 0ecbe37c9..000000000
--- a/app/javascript/themes/glitch/features/video/index.js
+++ /dev/null
@@ -1,288 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import { throttle } from 'lodash';
-import classNames from 'classnames';
-import { isFullscreen, requestFullscreen, exitFullscreen } from 'themes/glitch/util/fullscreen';
-
-const messages = defineMessages({
-  play: { id: 'video.play', defaultMessage: 'Play' },
-  pause: { id: 'video.pause', defaultMessage: 'Pause' },
-  mute: { id: 'video.mute', defaultMessage: 'Mute sound' },
-  unmute: { id: 'video.unmute', defaultMessage: 'Unmute sound' },
-  hide: { id: 'video.hide', defaultMessage: 'Hide video' },
-  expand: { id: 'video.expand', defaultMessage: 'Expand video' },
-  close: { id: 'video.close', defaultMessage: 'Close video' },
-  fullscreen: { id: 'video.fullscreen', defaultMessage: 'Full screen' },
-  exit_fullscreen: { id: 'video.exit_fullscreen', defaultMessage: 'Exit full screen' },
-});
-
-const findElementPosition = el => {
-  let box;
-
-  if (el.getBoundingClientRect && el.parentNode) {
-    box = el.getBoundingClientRect();
-  }
-
-  if (!box) {
-    return {
-      left: 0,
-      top: 0,
-    };
-  }
-
-  const docEl = document.documentElement;
-  const body  = document.body;
-
-  const clientLeft = docEl.clientLeft || body.clientLeft || 0;
-  const scrollLeft = window.pageXOffset || body.scrollLeft;
-  const left       = (box.left + scrollLeft) - clientLeft;
-
-  const clientTop = docEl.clientTop || body.clientTop || 0;
-  const scrollTop = window.pageYOffset || body.scrollTop;
-  const top       = (box.top + scrollTop) - clientTop;
-
-  return {
-    left: Math.round(left),
-    top: Math.round(top),
-  };
-};
-
-const getPointerPosition = (el, event) => {
-  const position = {};
-  const box = findElementPosition(el);
-  const boxW = el.offsetWidth;
-  const boxH = el.offsetHeight;
-  const boxY = box.top;
-  const boxX = box.left;
-
-  let pageY = event.pageY;
-  let pageX = event.pageX;
-
-  if (event.changedTouches) {
-    pageX = event.changedTouches[0].pageX;
-    pageY = event.changedTouches[0].pageY;
-  }
-
-  position.y = Math.max(0, Math.min(1, ((boxY - pageY) + boxH) / boxH));
-  position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW));
-
-  return position;
-};
-
-@injectIntl
-export default class Video extends React.PureComponent {
-
-  static propTypes = {
-    preview: PropTypes.string,
-    src: PropTypes.string.isRequired,
-    alt: PropTypes.string,
-    width: PropTypes.number,
-    height: PropTypes.number,
-    sensitive: PropTypes.bool,
-    startTime: PropTypes.number,
-    onOpenVideo: PropTypes.func,
-    onCloseVideo: PropTypes.func,
-    letterbox: PropTypes.bool,
-    fullwidth: PropTypes.bool,
-    intl: PropTypes.object.isRequired,
-  };
-
-  state = {
-    progress: 0,
-    paused: true,
-    dragging: false,
-    fullscreen: false,
-    hovered: false,
-    muted: false,
-    revealed: !this.props.sensitive,
-  };
-
-  setPlayerRef = c => {
-    this.player = c;
-  }
-
-  setVideoRef = c => {
-    this.video = c;
-  }
-
-  setSeekRef = c => {
-    this.seek = c;
-  }
-
-  handlePlay = () => {
-    this.setState({ paused: false });
-  }
-
-  handlePause = () => {
-    this.setState({ paused: true });
-  }
-
-  handleTimeUpdate = () => {
-    this.setState({ progress: 100 * (this.video.currentTime / this.video.duration) });
-  }
-
-  handleMouseDown = e => {
-    document.addEventListener('mousemove', this.handleMouseMove, true);
-    document.addEventListener('mouseup', this.handleMouseUp, true);
-    document.addEventListener('touchmove', this.handleMouseMove, true);
-    document.addEventListener('touchend', this.handleMouseUp, true);
-
-    this.setState({ dragging: true });
-    this.video.pause();
-    this.handleMouseMove(e);
-  }
-
-  handleMouseUp = () => {
-    document.removeEventListener('mousemove', this.handleMouseMove, true);
-    document.removeEventListener('mouseup', this.handleMouseUp, true);
-    document.removeEventListener('touchmove', this.handleMouseMove, true);
-    document.removeEventListener('touchend', this.handleMouseUp, true);
-
-    this.setState({ dragging: false });
-    this.video.play();
-  }
-
-  handleMouseMove = throttle(e => {
-    const { x } = getPointerPosition(this.seek, e);
-    this.video.currentTime = this.video.duration * x;
-    this.setState({ progress: x * 100 });
-  }, 60);
-
-  togglePlay = () => {
-    if (this.state.paused) {
-      this.video.play();
-    } else {
-      this.video.pause();
-    }
-  }
-
-  toggleFullscreen = () => {
-    if (isFullscreen()) {
-      exitFullscreen();
-    } else {
-      requestFullscreen(this.player);
-    }
-  }
-
-  componentDidMount () {
-    document.addEventListener('fullscreenchange', this.handleFullscreenChange, true);
-    document.addEventListener('webkitfullscreenchange', this.handleFullscreenChange, true);
-    document.addEventListener('mozfullscreenchange', this.handleFullscreenChange, true);
-    document.addEventListener('MSFullscreenChange', this.handleFullscreenChange, true);
-  }
-
-  componentWillUnmount () {
-    document.removeEventListener('fullscreenchange', this.handleFullscreenChange, true);
-    document.removeEventListener('webkitfullscreenchange', this.handleFullscreenChange, true);
-    document.removeEventListener('mozfullscreenchange', this.handleFullscreenChange, true);
-    document.removeEventListener('MSFullscreenChange', this.handleFullscreenChange, true);
-  }
-
-  handleFullscreenChange = () => {
-    this.setState({ fullscreen: isFullscreen() });
-  }
-
-  handleMouseEnter = () => {
-    this.setState({ hovered: true });
-  }
-
-  handleMouseLeave = () => {
-    this.setState({ hovered: false });
-  }
-
-  toggleMute = () => {
-    this.video.muted = !this.video.muted;
-    this.setState({ muted: this.video.muted });
-  }
-
-  toggleReveal = () => {
-    if (this.state.revealed) {
-      this.video.pause();
-    }
-
-    this.setState({ revealed: !this.state.revealed });
-  }
-
-  handleLoadedData = () => {
-    if (this.props.startTime) {
-      this.video.currentTime = this.props.startTime;
-      this.video.play();
-    }
-  }
-
-  handleProgress = () => {
-    if (this.video.buffered.length > 0) {
-      this.setState({ buffer: this.video.buffered.end(0) / this.video.duration * 100 });
-    }
-  }
-
-  handleOpenVideo = () => {
-    this.video.pause();
-    this.props.onOpenVideo(this.video.currentTime);
-  }
-
-  handleCloseVideo = () => {
-    this.video.pause();
-    this.props.onCloseVideo();
-  }
-
-  render () {
-    const { preview, src, width, height, startTime, onOpenVideo, onCloseVideo, intl, alt, letterbox, fullwidth } = this.props;
-    const { progress, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
-
-    return (
-      <div className={classNames('video-player', { inactive: !revealed, inline: !fullscreen, fullscreen, letterbox, 'full-width': fullwidth })} ref={this.setPlayerRef} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
-        <video
-          ref={this.setVideoRef}
-          src={src}
-          poster={preview}
-          preload={startTime ? 'auto' : 'none'}
-          loop
-          role='button'
-          tabIndex='0'
-          aria-label={alt}
-          width={width}
-          height={height}
-          onClick={this.togglePlay}
-          onPlay={this.handlePlay}
-          onPause={this.handlePause}
-          onTimeUpdate={this.handleTimeUpdate}
-          onLoadedData={this.handleLoadedData}
-          onProgress={this.handleProgress}
-        />
-
-        <button className={classNames('video-player__spoiler', { active: !revealed })} onClick={this.toggleReveal}>
-          <span className='video-player__spoiler__title'><FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' /></span>
-          <span className='video-player__spoiler__subtitle'><FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' /></span>
-        </button>
-
-        <div className={classNames('video-player__controls', { active: paused || hovered })}>
-          <div className='video-player__seek' onMouseDown={this.handleMouseDown} ref={this.setSeekRef}>
-            <div className='video-player__seek__buffer' style={{ width: `${buffer}%` }} />
-            <div className='video-player__seek__progress' style={{ width: `${progress}%` }} />
-
-            <span
-              className={classNames('video-player__seek__handle', { active: dragging })}
-              tabIndex='0'
-              style={{ left: `${progress}%` }}
-            />
-          </div>
-
-          <div className='video-player__buttons left'>
-            <button aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} onClick={this.togglePlay}><i className={classNames('fa fa-fw', { 'fa-play': paused, 'fa-pause': !paused })} /></button>
-            <button aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} onClick={this.toggleMute}><i className={classNames('fa fa-fw', { 'fa-volume-off': muted, 'fa-volume-up': !muted })} /></button>
-            {!onCloseVideo && <button aria-label={intl.formatMessage(messages.hide)} onClick={this.toggleReveal}><i className='fa fa-fw fa-eye' /></button>}
-          </div>
-
-          <div className='video-player__buttons right'>
-            {(!fullscreen && onOpenVideo) && <button aria-label={intl.formatMessage(messages.expand)} onClick={this.handleOpenVideo}><i className='fa fa-fw fa-expand' /></button>}
-            {onCloseVideo && <button aria-label={intl.formatMessage(messages.close)} onClick={this.handleCloseVideo}><i className='fa fa-fw fa-times' /></button>}
-            <button aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)} onClick={this.toggleFullscreen}><i className={classNames('fa fa-fw', { 'fa-arrows-alt': !fullscreen, 'fa-compress': fullscreen })} /></button>
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}