about summary refs log tree commit diff
path: root/app/assets/javascripts/components/components
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/components/components')
-rw-r--r--app/assets/javascripts/components/components/account.jsx91
-rw-r--r--app/assets/javascripts/components/components/attachment_list.jsx32
-rw-r--r--app/assets/javascripts/components/components/autosuggest_textarea.jsx211
-rw-r--r--app/assets/javascripts/components/components/avatar.jsx63
-rw-r--r--app/assets/javascripts/components/components/button.jsx49
-rw-r--r--app/assets/javascripts/components/components/collapsable.jsx20
-rw-r--r--app/assets/javascripts/components/components/column_back_button.jsx31
-rw-r--r--app/assets/javascripts/components/components/column_back_button_slim.jsx31
-rw-r--r--app/assets/javascripts/components/components/column_collapsable.jsx56
-rw-r--r--app/assets/javascripts/components/components/display_name.jsx24
-rw-r--r--app/assets/javascripts/components/components/dropdown_menu.jsx78
-rw-r--r--app/assets/javascripts/components/components/extended_video_player.jsx53
-rw-r--r--app/assets/javascripts/components/components/icon_button.jsx95
-rw-r--r--app/assets/javascripts/components/components/load_more.jsx14
-rw-r--r--app/assets/javascripts/components/components/loading_indicator.jsx9
-rw-r--r--app/assets/javascripts/components/components/media_gallery.jsx195
-rw-r--r--app/assets/javascripts/components/components/missing_indicator.jsx9
-rw-r--r--app/assets/javascripts/components/components/permalink.jsx36
-rw-r--r--app/assets/javascripts/components/components/relative_timestamp.jsx19
-rw-r--r--app/assets/javascripts/components/components/status.jsx121
-rw-r--r--app/assets/javascripts/components/components/status_action_bar.jsx137
-rw-r--r--app/assets/javascripts/components/components/status_content.jsx157
-rw-r--r--app/assets/javascripts/components/components/status_list.jsx128
-rw-r--r--app/assets/javascripts/components/components/video_player.jsx198
24 files changed, 0 insertions, 1857 deletions
diff --git a/app/assets/javascripts/components/components/account.jsx b/app/assets/javascripts/components/components/account.jsx
deleted file mode 100644
index 81439bd25..000000000
--- a/app/assets/javascripts/components/components/account.jsx
+++ /dev/null
@@ -1,91 +0,0 @@
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import Avatar from './avatar';
-import DisplayName from './display_name';
-import Permalink from './permalink';
-import IconButton from './icon_button';
-import { defineMessages, injectIntl } from 'react-intl';
-
-const messages = defineMessages({
-  follow: { id: 'account.follow', defaultMessage: 'Follow' },
-  unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
-  requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' },
-  unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
-  unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' }
-});
-
-class Account extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.handleFollow = this.handleFollow.bind(this);
-    this.handleBlock = this.handleBlock.bind(this);
-    this.handleMute = this.handleMute.bind(this);
-  }
-
-  handleFollow () {
-    this.props.onFollow(this.props.account);
-  }
-
-  handleBlock () {
-    this.props.onBlock(this.props.account);
-  }
-
-  handleMute () {
-    this.props.onMute(this.props.account);
-  }
-
-  render () {
-    const { account, me, intl } = this.props;
-
-    if (!account) {
-      return <div />;
-    }
-
-    let buttons;
-
-    if (account.get('id') !== me && account.get('relationship', null) !== null) {
-      const following = account.getIn(['relationship', 'following']);
-      const requested = account.getIn(['relationship', 'requested']);
-      const blocking  = account.getIn(['relationship', 'blocking']);
-      const muting  = account.getIn(['relationship', 'muting']);
-
-      if (requested) {
-        buttons = <IconButton disabled={true} icon='hourglass' title={intl.formatMessage(messages.requested)} />
-      } else if (blocking) {
-        buttons = <IconButton active={true} icon='unlock-alt' title={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.handleBlock} />;
-      } else if (muting) {
-        buttons = <IconButton active={true} icon='volume-up' title={intl.formatMessage(messages.unmute, { name: account.get('username') })} onClick={this.handleMute} />;
-      } else {
-        buttons = <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} active={following} />;
-      }
-    }
-
-    return (
-      <div className='account'>
-        <div className='account__wrapper'>
-          <Permalink key={account.get('id')} className='account__display-name' href={account.get('url')} to={`/accounts/${account.get('id')}`}>
-            <div className='account__avatar-wrapper'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={36} /></div>
-            <DisplayName account={account} />
-          </Permalink>
-
-          <div className='account__relationship'>
-            {buttons}
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
-
-Account.propTypes = {
-  account: ImmutablePropTypes.map.isRequired,
-  me: PropTypes.number.isRequired,
-  onFollow: PropTypes.func.isRequired,
-  onBlock: PropTypes.func.isRequired,
-  onMute: PropTypes.func.isRequired,
-  intl: PropTypes.object.isRequired
-}
-
-export default injectIntl(Account);
diff --git a/app/assets/javascripts/components/components/attachment_list.jsx b/app/assets/javascripts/components/components/attachment_list.jsx
deleted file mode 100644
index 54841fa51..000000000
--- a/app/assets/javascripts/components/components/attachment_list.jsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import ImmutablePropTypes from 'react-immutable-proptypes';
-
-const filename = url => url.split('/').pop().split('#')[0].split('?')[0];
-
-class AttachmentList extends React.PureComponent {
-
-  render () {
-    const { media } = this.props;
-
-    return (
-      <div className='attachment-list'>
-        <div className='attachment-list__icon'>
-          <i className='fa fa-link' />
-        </div>
-
-        <ul className='attachment-list__list'>
-          {media.map(attachment =>
-            <li key={attachment.get('id')}>
-              <a href={attachment.get('remote_url')} target='_blank' rel='noopener'>{filename(attachment.get('remote_url'))}</a>
-            </li>
-          )}
-        </ul>
-      </div>
-    );
-  }
-}
-
-AttachmentList.propTypes = {
-  media: ImmutablePropTypes.list.isRequired
-};
-
-export default AttachmentList;
diff --git a/app/assets/javascripts/components/components/autosuggest_textarea.jsx b/app/assets/javascripts/components/components/autosuggest_textarea.jsx
deleted file mode 100644
index 9a4d5b7e3..000000000
--- a/app/assets/javascripts/components/components/autosuggest_textarea.jsx
+++ /dev/null
@@ -1,211 +0,0 @@
-import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import { isRtl } from '../rtl';
-
-const textAtCursorMatchesToken = (str, caretPosition) => {
-  let word;
-
-  let left  = str.slice(0, caretPosition).search(/\S+$/);
-  let right = str.slice(caretPosition).search(/\s/);
-
-  if (right < 0) {
-    word = str.slice(left);
-  } else {
-    word = str.slice(left, right + caretPosition);
-  }
-
-  if (!word || word.trim().length < 2 || word[0] !== '@') {
-    return [null, null];
-  }
-
-  word = word.trim().toLowerCase().slice(1);
-
-  if (word.length > 0) {
-    return [left + 1, word];
-  } else {
-    return [null, null];
-  }
-};
-
-class AutosuggestTextarea extends React.Component {
-
-  constructor (props, context) {
-    super(props, context);
-    this.state = {
-      suggestionsHidden: false,
-      selectedSuggestion: 0,
-      lastToken: null,
-      tokenStart: 0
-    };
-    this.onChange = this.onChange.bind(this);
-    this.onKeyDown = this.onKeyDown.bind(this);
-    this.onBlur = this.onBlur.bind(this);
-    this.onSuggestionClick = this.onSuggestionClick.bind(this);
-    this.setTextarea = this.setTextarea.bind(this);
-    this.onPaste = this.onPaste.bind(this);
-  }
-
-  onChange (e) {
-    const [ tokenStart, token ] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart);
-
-    if (token !== null && this.state.lastToken !== token) {
-      this.setState({ lastToken: token, selectedSuggestion: 0, tokenStart });
-      this.props.onSuggestionsFetchRequested(token);
-    } else if (token === null) {
-      this.setState({ lastToken: null });
-      this.props.onSuggestionsClearRequested();
-    }
-
-    // auto-resize textarea
-    e.target.style.height = `${e.target.scrollHeight}px`;
-
-    this.props.onChange(e);
-  }
-
-  onKeyDown (e) {
-    const { suggestions, disabled } = this.props;
-    const { selectedSuggestion, suggestionsHidden } = this.state;
-
-    if (disabled) {
-      e.preventDefault();
-      return;
-    }
-
-    switch(e.key) {
-    case 'Escape':
-      if (!suggestionsHidden) {
-        e.preventDefault();
-        this.setState({ suggestionsHidden: true });
-      }
-
-      break;
-    case 'ArrowDown':
-      if (suggestions.size > 0 && !suggestionsHidden) {
-        e.preventDefault();
-        this.setState({ selectedSuggestion: Math.min(selectedSuggestion + 1, suggestions.size - 1) });
-      }
-
-      break;
-    case 'ArrowUp':
-      if (suggestions.size > 0 && !suggestionsHidden) {
-        e.preventDefault();
-        this.setState({ selectedSuggestion: Math.max(selectedSuggestion - 1, 0) });
-      }
-
-      break;
-    case 'Enter':
-    case 'Tab':
-      // Select suggestion
-      if (this.state.lastToken !== null && suggestions.size > 0 && !suggestionsHidden) {
-        e.preventDefault();
-        e.stopPropagation();
-        this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestions.get(selectedSuggestion));
-      }
-
-      break;
-    }
-
-    if (e.defaultPrevented || !this.props.onKeyDown) {
-      return;
-    }
-
-    this.props.onKeyDown(e);
-  }
-
-  onBlur () {
-    // If we hide the suggestions immediately, then this will prevent the
-    // onClick for the suggestions themselves from firing.
-    // Setting a short window for that to take place before hiding the
-    // suggestions ensures that can't happen.
-    setTimeout(() => {
-      this.setState({ suggestionsHidden: true });
-    }, 100);
-  }
-
-  onSuggestionClick (suggestion, e) {
-    e.preventDefault();
-    this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
-    this.textarea.focus();
-  }
-
-  componentWillReceiveProps (nextProps) {
-    if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden) {
-      this.setState({ suggestionsHidden: false });
-    }
-  }
-
-  setTextarea (c) {
-    this.textarea = c;
-  }
-
-  onPaste (e) {
-    if (e.clipboardData && e.clipboardData.files.length === 1) {
-      this.props.onPaste(e.clipboardData.files)
-      e.preventDefault();
-    }
-  }
-
-  reset () {
-    this.textarea.style.height = 'auto';
-  }
-
-  render () {
-    const { value, suggestions, disabled, placeholder, onKeyUp } = this.props;
-    const { suggestionsHidden, selectedSuggestion } = this.state;
-    const style = { direction: 'ltr' };
-
-    if (isRtl(value)) {
-      style.direction = 'rtl';
-    }
-
-    return (
-      <div className='autosuggest-textarea'>
-        <textarea
-          ref={this.setTextarea}
-          className='autosuggest-textarea__textarea'
-          disabled={disabled}
-          placeholder={placeholder}
-          autoFocus={true}
-          value={value}
-          onChange={this.onChange}
-          onKeyDown={this.onKeyDown}
-          onKeyUp={onKeyUp}
-          onBlur={this.onBlur}
-          onPaste={this.onPaste}
-          style={style}
-        />
-
-        <div style={{ display: (suggestions.size > 0 && !suggestionsHidden) ? 'block' : 'none' }} className='autosuggest-textarea__suggestions'>
-          {suggestions.map((suggestion, i) => (
-            <div
-              role='button'
-              tabIndex='0'
-              key={suggestion}
-              className={`autosuggest-textarea__suggestions__item ${i === selectedSuggestion ? 'selected' : ''}`}
-              onClick={this.onSuggestionClick.bind(this, suggestion)}>
-              <AutosuggestAccountContainer id={suggestion} />
-            </div>
-          ))}
-        </div>
-      </div>
-    );
-  }
-
-};
-
-AutosuggestTextarea.propTypes = {
-  value: PropTypes.string,
-  suggestions: ImmutablePropTypes.list,
-  disabled: PropTypes.bool,
-  placeholder: PropTypes.string,
-  onSuggestionSelected: PropTypes.func.isRequired,
-  onSuggestionsClearRequested: PropTypes.func.isRequired,
-  onSuggestionsFetchRequested: PropTypes.func.isRequired,
-  onChange: PropTypes.func.isRequired,
-  onKeyUp: PropTypes.func,
-  onKeyDown: PropTypes.func,
-  onPaste: PropTypes.func.isRequired,
-};
-
-export default AutosuggestTextarea;
diff --git a/app/assets/javascripts/components/components/avatar.jsx b/app/assets/javascripts/components/components/avatar.jsx
deleted file mode 100644
index d1a00ac39..000000000
--- a/app/assets/javascripts/components/components/avatar.jsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import PropTypes from 'prop-types';
-
-class Avatar extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.state = {
-      hovering: false
-    };
-    this.handleMouseEnter = this.handleMouseEnter.bind(this);
-    this.handleMouseLeave = this.handleMouseLeave.bind(this);
-  }
-
-  handleMouseEnter () {
-    this.setState({ hovering: true });
-  }
-
-  handleMouseLeave () {
-    this.setState({ hovering: false });
-  }
-
-  render () {
-    const { src, size, staticSrc, animate } = this.props;
-    const { hovering } = this.state;
-
-    const style = {
-      ...this.props.style,
-      width: `${size}px`,
-      height: `${size}px`,
-      backgroundSize: `${size}px ${size}px`
-    };
-
-    if (hovering || animate) {
-      style.backgroundImage = `url(${src})`;
-    } else {
-      style.backgroundImage = `url(${staticSrc})`;
-    }
-
-    return (
-      <div
-        className='account__avatar'
-        onMouseEnter={this.handleMouseEnter}
-        onMouseLeave={this.handleMouseLeave}
-        style={style}
-      />
-    );
-  }
-
-}
-
-Avatar.propTypes = {
-  src: PropTypes.string.isRequired,
-  staticSrc: PropTypes.string,
-  size: PropTypes.number.isRequired,
-  style: PropTypes.object,
-  animate: PropTypes.bool
-};
-
-Avatar.defaultProps = {
-  animate: false
-};
-
-export default Avatar;
diff --git a/app/assets/javascripts/components/components/button.jsx b/app/assets/javascripts/components/components/button.jsx
deleted file mode 100644
index 00d80b1fd..000000000
--- a/app/assets/javascripts/components/components/button.jsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import PropTypes from 'prop-types';
-
-class Button extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.handleClick = this.handleClick.bind(this);
-  }
-
-  handleClick (e) {
-    if (!this.props.disabled) {
-      this.props.onClick();
-    }
-  }
-
-  render () {
-    const style = {
-      display: this.props.block ? 'block' : 'inline-block',
-      width: this.props.block ? '100%' : 'auto',
-      padding: `0 ${this.props.size / 2.25}px`,
-      height: `${this.props.size}px`,
-      lineHeight: `${this.props.size}px`
-    };
-
-    return (
-      <button className={`button ${this.props.secondary ? 'button-secondary' : ''}`} disabled={this.props.disabled} onClick={this.handleClick} style={{ ...style, ...this.props.style }}>
-        {this.props.text || this.props.children}
-      </button>
-    );
-  }
-
-}
-
-Button.propTypes = {
-  text: PropTypes.node,
-  onClick: PropTypes.func,
-  disabled: PropTypes.bool,
-  block: PropTypes.bool,
-  secondary: PropTypes.bool,
-  size: PropTypes.number,
-  style: PropTypes.object,
-  children: PropTypes.node
-};
-
-Button.defaultProps = {
-  size: 36
-};
-
-export default Button;
diff --git a/app/assets/javascripts/components/components/collapsable.jsx b/app/assets/javascripts/components/components/collapsable.jsx
deleted file mode 100644
index 0810768f0..000000000
--- a/app/assets/javascripts/components/components/collapsable.jsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import { Motion, spring } from 'react-motion';
-import PropTypes from 'prop-types';
-
-const Collapsable = ({ fullHeight, isVisible, children }) => (
-  <Motion defaultStyle={{ opacity: !isVisible ? 0 : 100, height: isVisible ? fullHeight : 0 }} style={{ opacity: spring(!isVisible ? 0 : 100), height: spring(!isVisible ? 0 : fullHeight) }}>
-    {({ opacity, height }) =>
-      <div style={{ height: `${height}px`, overflow: 'hidden', opacity: opacity / 100, display: Math.floor(opacity) === 0 ? 'none' : 'block' }}>
-        {children}
-      </div>
-    }
-  </Motion>
-);
-
-Collapsable.propTypes = {
-  fullHeight: PropTypes.number.isRequired,
-  isVisible: PropTypes.bool.isRequired,
-  children: PropTypes.node.isRequired
-};
-
-export default Collapsable;
diff --git a/app/assets/javascripts/components/components/column_back_button.jsx b/app/assets/javascripts/components/components/column_back_button.jsx
deleted file mode 100644
index 70110f0aa..000000000
--- a/app/assets/javascripts/components/components/column_back_button.jsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import { FormattedMessage } from 'react-intl';
-import PropTypes from 'prop-types';
-
-class ColumnBackButton extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.handleClick = this.handleClick.bind(this);
-  }
-
-  handleClick () {
-    if (window.history && window.history.length === 1) this.context.router.push("/");
-    else this.context.router.goBack();
-  }
-
-  render () {
-    return (
-      <div role='button' tabIndex='0' onClick={this.handleClick} className='column-back-button'>
-        <i className='fa fa-fw fa-chevron-left column-back-button__icon'/>
-        <FormattedMessage id='column_back_button.label' defaultMessage='Back' />
-      </div>
-    );
-  }
-
-};
-
-ColumnBackButton.contextTypes = {
-  router: PropTypes.object
-};
-
-export default ColumnBackButton;
diff --git a/app/assets/javascripts/components/components/column_back_button_slim.jsx b/app/assets/javascripts/components/components/column_back_button_slim.jsx
deleted file mode 100644
index 719690097..000000000
--- a/app/assets/javascripts/components/components/column_back_button_slim.jsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import { FormattedMessage } from 'react-intl';
-import PropTypes from 'prop-types';
-
-class ColumnBackButtonSlim extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.handleClick = this.handleClick.bind(this);
-  }
-
-  handleClick () {
-    this.context.router.push('/');
-  }
-
-  render () {
-    return (
-      <div className='column-back-button--slim'>
-        <div role='button' tabIndex='0' onClick={this.handleClick} className='column-back-button column-back-button--slim-button'>
-          <i className='fa fa-fw fa-chevron-left column-back-button__icon' />
-          <FormattedMessage id='column_back_button.label' defaultMessage='Back' />
-        </div>
-      </div>
-    );
-  }
-}
-
-ColumnBackButtonSlim.contextTypes = {
-  router: PropTypes.object
-};
-
-export default ColumnBackButtonSlim;
diff --git a/app/assets/javascripts/components/components/column_collapsable.jsx b/app/assets/javascripts/components/components/column_collapsable.jsx
deleted file mode 100644
index 2cb440862..000000000
--- a/app/assets/javascripts/components/components/column_collapsable.jsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { Motion, spring } from 'react-motion';
-import PropTypes from 'prop-types';
-
-class ColumnCollapsable extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.state = {
-      collapsed: true
-    };
-
-    this.handleToggleCollapsed = this.handleToggleCollapsed.bind(this);
-  }
-
-  handleToggleCollapsed () {
-    const currentState = this.state.collapsed;
-
-    this.setState({ collapsed: !currentState });
-
-    if (!currentState && this.props.onCollapse) {
-      this.props.onCollapse();
-    }
-  }
-
-  render () {
-    const { icon, title, fullHeight, children } = this.props;
-    const { collapsed } = this.state;
-    const collapsedClassName = collapsed ? 'collapsable-collapsed' : 'collapsable';
-
-    return (
-      <div className='column-collapsable'>
-        <div role='button' tabIndex='0' title={`${title}`} className={`column-icon ${collapsedClassName}`} onClick={this.handleToggleCollapsed}>
-          <i className={`fa fa-${icon}`} />
-        </div>
-
-        <Motion defaultStyle={{ opacity: 0, height: 0 }} style={{ opacity: spring(collapsed ? 0 : 100), height: spring(collapsed ? 0 : fullHeight, collapsed ? undefined : { stiffness: 150, damping: 9 }) }}>
-          {({ opacity, height }) =>
-            <div style={{ overflow: height === fullHeight ? 'auto' : 'hidden', height: `${height}px`, opacity: opacity / 100, maxHeight: '70vh' }}>
-              {children}
-            </div>
-          }
-        </Motion>
-      </div>
-    );
-  }
-}
-
-ColumnCollapsable.propTypes = {
-  icon: PropTypes.string.isRequired,
-  title: PropTypes.string,
-  fullHeight: PropTypes.number.isRequired,
-  children: PropTypes.node,
-  onCollapse: PropTypes.func
-};
-
-export default ColumnCollapsable;
diff --git a/app/assets/javascripts/components/components/display_name.jsx b/app/assets/javascripts/components/components/display_name.jsx
deleted file mode 100644
index d7257e092..000000000
--- a/app/assets/javascripts/components/components/display_name.jsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import escapeTextContentForBrowser from 'escape-html';
-import emojify from '../emoji';
-
-class DisplayName extends React.PureComponent {
-
-  render () {
-    const displayName     = this.props.account.get('display_name').length === 0 ? this.props.account.get('username') : this.props.account.get('display_name');
-    const displayNameHTML = { __html: emojify(escapeTextContentForBrowser(displayName)) };
-
-    return (
-      <span className='display-name'>
-        <strong className='display-name__html' dangerouslySetInnerHTML={displayNameHTML} /> <span className='display-name__account'>@{this.props.account.get('acct')}</span>
-      </span>
-    );
-  }
-
-};
-
-DisplayName.propTypes = {
-  account: ImmutablePropTypes.map.isRequired
-}
-
-export default DisplayName;
diff --git a/app/assets/javascripts/components/components/dropdown_menu.jsx b/app/assets/javascripts/components/components/dropdown_menu.jsx
deleted file mode 100644
index f5ee27a11..000000000
--- a/app/assets/javascripts/components/components/dropdown_menu.jsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import Dropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown';
-import PropTypes from 'prop-types';
-
-class DropdownMenu extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.state = {
-      direction: 'left'
-    };
-    this.setRef = this.setRef.bind(this);
-    this.renderItem = this.renderItem.bind(this);
-  }
-
-  setRef (c) {
-    this.dropdown = c;
-  }
-
-  handleClick (i, e) {
-    const { action } = this.props.items[i];
-
-    if (typeof action === 'function') {
-      e.preventDefault();
-      action();
-      this.dropdown.hide();
-    }
-  }
-
-  renderItem (item, i) {
-    if (item === null) {
-      return <li key={ 'sep' + i } className='dropdown__sep' />;
-    }
-
-    const { text, action, href = '#' } = item;
-
-    return (
-      <li className='dropdown__content-list-item' key={ text + i }>
-        <a href={href} target='_blank' rel='noopener' onClick={this.handleClick.bind(this, i)} className='dropdown__content-list-link'>
-          {text}
-        </a>
-      </li>
-    );
-  }
-
-  render () {
-    const { icon, items, size, direction, ariaLabel } = this.props;
-    const directionClass = (direction === "left") ? "dropdown__left" : "dropdown__right";
-
-    return (
-      <Dropdown ref={this.setRef}>
-        <DropdownTrigger className='icon-button' style={{ fontSize: `${size}px`, width: `${size}px`, lineHeight: `${size}px` }} aria-label={ariaLabel}>
-          <i className={ `fa fa-fw fa-${icon} dropdown__icon` }  aria-hidden={true} />
-        </DropdownTrigger>
-
-        <DropdownContent className={directionClass}>
-          <ul className='dropdown__content-list'>
-            {items.map(this.renderItem)}
-          </ul>
-        </DropdownContent>
-      </Dropdown>
-    );
-  }
-
-}
-
-DropdownMenu.propTypes = {
-  icon: PropTypes.string.isRequired,
-  items: PropTypes.array.isRequired,
-  size: PropTypes.number.isRequired,
-  direction: PropTypes.string,
-  ariaLabel: PropTypes.string
-};
-
-DropdownMenu.defaultProps = {
-  ariaLabel: "Menu"
-};
-
-export default DropdownMenu;
diff --git a/app/assets/javascripts/components/components/extended_video_player.jsx b/app/assets/javascripts/components/components/extended_video_player.jsx
deleted file mode 100644
index bbbe09da8..000000000
--- a/app/assets/javascripts/components/components/extended_video_player.jsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import PropTypes from 'prop-types';
-
-class ExtendedVideoPlayer extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.handleLoadedData = this.handleLoadedData.bind(this);
-    this.setRef = this.setRef.bind(this);
-  }
-
-  handleLoadedData () {
-    if (this.props.time) {
-      this.video.currentTime = this.props.time;
-    }
-  }
-
-  componentDidMount () {
-    this.video.addEventListener('loadeddata', this.handleLoadedData);
-  }
-
-  componentWillUnmount () {
-    this.video.removeEventListener('loadeddata', this.handleLoadedData);
-  }
-
-  setRef (c) {
-    this.video = c;
-  }
-
-  render () {
-    return (
-      <div className='extended-video-player'>
-        <video
-          ref={this.setRef}
-          src={this.props.src}
-          autoPlay
-          muted={this.props.muted}
-          controls={this.props.controls}
-          loop={!this.props.controls}
-        />
-      </div>
-    );
-  }
-
-}
-
-ExtendedVideoPlayer.propTypes = {
-  src: PropTypes.string.isRequired,
-  time: PropTypes.number,
-  controls: PropTypes.bool.isRequired,
-  muted: PropTypes.bool.isRequired
-};
-
-export default ExtendedVideoPlayer;
diff --git a/app/assets/javascripts/components/components/icon_button.jsx b/app/assets/javascripts/components/components/icon_button.jsx
deleted file mode 100644
index 67c6513fd..000000000
--- a/app/assets/javascripts/components/components/icon_button.jsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import { Motion, spring } from 'react-motion';
-import PropTypes from 'prop-types';
-
-class IconButton extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.handleClick = this.handleClick.bind(this);
-  }
-
-  handleClick (e) {
-    e.preventDefault();
-
-    if (!this.props.disabled) {
-      this.props.onClick(e);
-    }
-  }
-
-  render () {
-    let style = {
-      fontSize: `${this.props.size}px`,
-      width: `${this.props.size * 1.28571429}px`,
-      height: `${this.props.size * 1.28571429}px`,
-      lineHeight: `${this.props.size}px`,
-      ...this.props.style
-    };
-
-    if (this.props.active) {
-      style = { ...style, ...this.props.activeStyle };
-    }
-
-    const classes = ['icon-button'];
-
-    if (this.props.active) {
-      classes.push('active');
-    }
-
-    if (this.props.disabled) {
-      classes.push('disabled');
-    }
-
-    if (this.props.inverted) {
-      classes.push('inverted');
-    }
-
-    if (this.props.overlay) {
-      classes.push('overlayed');
-    }
-
-    if (this.props.className) {
-      classes.push(this.props.className)
-    }
-
-    return (
-      <Motion defaultStyle={{ rotate: this.props.active ? -360 : 0 }} style={{ rotate: this.props.animate ? spring(this.props.active ? -360 : 0, { stiffness: 120, damping: 7 }) : 0 }}>
-        {({ rotate }) =>
-          <button
-            aria-label={this.props.title}
-            title={this.props.title}
-            className={classes.join(' ')}
-            onClick={this.handleClick}
-            style={style}>
-            <i style={{ transform: `rotate(${rotate}deg)` }} className={`fa fa-fw fa-${this.props.icon}`} aria-hidden='true' />
-          </button>
-        }
-      </Motion>
-    );
-  }
-
-}
-
-IconButton.propTypes = {
-  className: PropTypes.string,
-  title: PropTypes.string.isRequired,
-  icon: PropTypes.string.isRequired,
-  onClick: PropTypes.func,
-  size: PropTypes.number,
-  active: PropTypes.bool,
-  style: PropTypes.object,
-  activeStyle: PropTypes.object,
-  disabled: PropTypes.bool,
-  inverted: PropTypes.bool,
-  animate: PropTypes.bool,
-  overlay: PropTypes.bool
-};
-
-IconButton.defaultProps = {
-  size: 18,
-  active: false,
-  disabled: false,
-  animate: false,
-  overlay: false
-};
-
-export default IconButton;
diff --git a/app/assets/javascripts/components/components/load_more.jsx b/app/assets/javascripts/components/components/load_more.jsx
deleted file mode 100644
index f59ff1103..000000000
--- a/app/assets/javascripts/components/components/load_more.jsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import { FormattedMessage } from 'react-intl';
-import PropTypes from 'prop-types';
-
-const LoadMore = ({ onClick }) => (
-  <a href="#" className='load-more' role='button' onClick={onClick}>
-    <FormattedMessage id='status.load_more' defaultMessage='Load more' />
-  </a>
-);
-
-LoadMore.propTypes = {
-  onClick: PropTypes.func
-};
-
-export default LoadMore;
diff --git a/app/assets/javascripts/components/components/loading_indicator.jsx b/app/assets/javascripts/components/components/loading_indicator.jsx
deleted file mode 100644
index 61e0a0f15..000000000
--- a/app/assets/javascripts/components/components/loading_indicator.jsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import { FormattedMessage } from 'react-intl';
-
-const LoadingIndicator = () => (
-  <div className='loading-indicator'>
-    <FormattedMessage id='loading_indicator.label' defaultMessage='Loading...' />
-  </div>
-);
-
-export default LoadingIndicator;
diff --git a/app/assets/javascripts/components/components/media_gallery.jsx b/app/assets/javascripts/components/components/media_gallery.jsx
deleted file mode 100644
index 58e134f50..000000000
--- a/app/assets/javascripts/components/components/media_gallery.jsx
+++ /dev/null
@@ -1,195 +0,0 @@
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import IconButton from './icon_button';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import { isIOS } from '../is_mobile';
-
-const messages = defineMessages({
-  toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' }
-});
-
-class Item extends React.PureComponent {
-  constructor (props, context) {
-    super(props, context);
-    this.handleClick = this.handleClick.bind(this);
-  }
-
-  handleClick (e) {
-    const { index, onClick } = this.props;
-
-    if (e.button === 0) {
-      e.preventDefault();
-      onClick(index);
-    }
-
-    e.stopPropagation();
-  }
-
-  render () {
-    const { attachment, index, size } = this.props;
-
-    let width  = 50;
-    let height = 100;
-    let top    = 'auto';
-    let left   = 'auto';
-    let bottom = 'auto';
-    let right  = 'auto';
-
-    if (size === 1) {
-      width = 100;
-    }
-
-    if (size === 4 || (size === 3 && index > 0)) {
-      height = 50;
-    }
-
-    if (size === 2) {
-      if (index === 0) {
-        right = '2px';
-      } else {
-        left = '2px';
-      }
-    } else if (size === 3) {
-      if (index === 0) {
-        right = '2px';
-      } else if (index > 0) {
-        left = '2px';
-      }
-
-      if (index === 1) {
-        bottom = '2px';
-      } else if (index > 1) {
-        top = '2px';
-      }
-    } else if (size === 4) {
-      if (index === 0 || index === 2) {
-        right = '2px';
-      }
-
-      if (index === 1 || index === 3) {
-        left = '2px';
-      }
-
-      if (index < 2) {
-        bottom = '2px';
-      } else {
-        top = '2px';
-      }
-    }
-
-    let thumbnail = '';
-
-    if (attachment.get('type') === 'image') {
-      thumbnail = (
-        <a
-          className='media-gallery__item-thumbnail'
-          href={attachment.get('remote_url') || attachment.get('url')}
-          onClick={this.handleClick}
-          target='_blank'
-          style={{ backgroundImage: `url(${attachment.get('preview_url')})` }}
-        />
-      );
-    } else if (attachment.get('type') === 'gifv') {
-      const autoPlay = !isIOS() && this.props.autoPlayGif;
-
-      thumbnail = (
-        <div className={`media-gallery__gifv ${autoPlay ? 'autoplay' : ''}`}>
-          <video
-            className='media-gallery__item-gifv-thumbnail'
-            role='application'
-            src={attachment.get('url')}
-            onClick={this.handleClick}
-            autoPlay={autoPlay}
-            loop={true}
-            muted={true}
-          />
-
-          <span className='media-gallery__gifv__label'>GIF</span>
-        </div>
-      );
-    }
-
-    return (
-      <div className='media-gallery__item' key={attachment.get('id')} style={{ left: left, top: top, right: right, bottom: bottom, width: `${width}%`, height: `${height}%` }}>
-        {thumbnail}
-      </div>
-    );
-  }
-
-}
-
-Item.propTypes = {
-  attachment: ImmutablePropTypes.map.isRequired,
-  index: PropTypes.number.isRequired,
-  size: PropTypes.number.isRequired,
-  onClick: PropTypes.func.isRequired,
-  autoPlayGif: PropTypes.bool.isRequired
-};
-
-class MediaGallery extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.state = {
-      visible: !props.sensitive
-    };
-    this.handleOpen = this.handleOpen.bind(this);
-    this.handleClick = this.handleClick.bind(this);
-  }
-
-  handleOpen (e) {
-    this.setState({ visible: !this.state.visible });
-  }
-
-  handleClick (index) {
-    this.props.onOpenMedia(this.props.media, index);
-  }
-
-  render () {
-    const { media, intl, sensitive } = this.props;
-
-    let children;
-
-    if (!this.state.visible) {
-      let warning;
-
-      if (sensitive) {
-        warning = <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />;
-      } else {
-        warning = <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />;
-      }
-
-      children = (
-        <div role='button' tabIndex='0' className='media-spoiler' onClick={this.handleOpen}>
-          <span className='media-spoiler__warning'>{warning}</span>
-          <span className='media-spoiler__trigger'><FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' /></span>
-        </div>
-      );
-    } else {
-      const size = media.take(4).size;
-      children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} autoPlayGif={this.props.autoPlayGif} index={i} size={size} />);
-    }
-
-    return (
-      <div className='media-gallery' style={{ height: `${this.props.height}px` }}>
-        <div className='spoiler-button' style={{ display: !this.state.visible ? 'none' : 'block' }}>
-          <IconButton title={intl.formatMessage(messages.toggle_visible)} icon={this.state.visible ? 'eye' : 'eye-slash'} overlay onClick={this.handleOpen} />
-        </div>
-
-        {children}
-      </div>
-    );
-  }
-
-}
-
-MediaGallery.propTypes = {
-  sensitive: PropTypes.bool,
-  media: ImmutablePropTypes.list.isRequired,
-  height: PropTypes.number.isRequired,
-  onOpenMedia: PropTypes.func.isRequired,
-  intl: PropTypes.object.isRequired,
-  autoPlayGif: PropTypes.bool.isRequired
-};
-
-export default injectIntl(MediaGallery);
diff --git a/app/assets/javascripts/components/components/missing_indicator.jsx b/app/assets/javascripts/components/components/missing_indicator.jsx
deleted file mode 100644
index 75129ae14..000000000
--- a/app/assets/javascripts/components/components/missing_indicator.jsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import { FormattedMessage } from 'react-intl';
-
-const MissingIndicator = () => (
-  <div className='missing-indicator'>
-    <FormattedMessage id='missing_indicator.label' defaultMessage='Not found' />
-  </div>
-);
-
-export default MissingIndicator;
diff --git a/app/assets/javascripts/components/components/permalink.jsx b/app/assets/javascripts/components/components/permalink.jsx
deleted file mode 100644
index ccbe4944f..000000000
--- a/app/assets/javascripts/components/components/permalink.jsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import PropTypes from 'prop-types';
-
-class Permalink extends React.Component {
-
-  constructor (props, context) {
-    super(props, context);
-    this.handleClick = this.handleClick.bind(this);
-  }
-
-  handleClick (e) {
-    if (e.button === 0) {
-      e.preventDefault();
-      this.context.router.push(this.props.to);
-    }
-  }
-
-  render () {
-    const { href, children, className, ...other } = this.props;
-
-    return <a href={href} onClick={this.handleClick} {...other} className={'permalink ' + className}>{children}</a>;
-  }
-
-}
-
-Permalink.contextTypes = {
-  router: PropTypes.object
-};
-
-Permalink.propTypes = {
-  className: PropTypes.string,
-  href: PropTypes.string.isRequired,
-  to: PropTypes.string.isRequired,
-  children: PropTypes.node
-};
-
-export default Permalink;
diff --git a/app/assets/javascripts/components/components/relative_timestamp.jsx b/app/assets/javascripts/components/components/relative_timestamp.jsx
deleted file mode 100644
index 9ab472e2c..000000000
--- a/app/assets/javascripts/components/components/relative_timestamp.jsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { injectIntl, FormattedRelative } from 'react-intl';
-import PropTypes from 'prop-types';
-
-const RelativeTimestamp = ({ intl, timestamp }) => {
-  const date = new Date(timestamp);
-
-  return (
-    <time dateTime={timestamp} title={intl.formatDate(date, { hour12: false, year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' })}>
-      <FormattedRelative value={date} />
-    </time>
-  );
-};
-
-RelativeTimestamp.propTypes = {
-  intl: PropTypes.object.isRequired,
-  timestamp: PropTypes.string.isRequired
-};
-
-export default injectIntl(RelativeTimestamp);
diff --git a/app/assets/javascripts/components/components/status.jsx b/app/assets/javascripts/components/components/status.jsx
deleted file mode 100644
index 193231837..000000000
--- a/app/assets/javascripts/components/components/status.jsx
+++ /dev/null
@@ -1,121 +0,0 @@
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import Avatar from './avatar';
-import RelativeTimestamp from './relative_timestamp';
-import DisplayName from './display_name';
-import MediaGallery from './media_gallery';
-import VideoPlayer from './video_player';
-import AttachmentList from './attachment_list';
-import StatusContent from './status_content';
-import StatusActionBar from './status_action_bar';
-import { FormattedMessage } from 'react-intl';
-import emojify from '../emoji';
-import escapeTextContentForBrowser from 'escape-html';
-
-class Status extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.handleClick = this.handleClick.bind(this);
-    this.handleAccountClick = this.handleAccountClick.bind(this);
-  }
-
-  handleClick () {
-    const { status } = this.props;
-    this.context.router.push(`/statuses/${status.getIn(['reblog', 'id'], status.get('id'))}`);
-  }
-
-  handleAccountClick (id, e) {
-    if (e.button === 0) {
-      e.preventDefault();
-      this.context.router.push(`/accounts/${id}`);
-    }
-  }
-
-  render () {
-    let media = '';
-    const { status, ...other } = this.props;
-
-    if (status === null) {
-      return <div />;
-    }
-
-    if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
-      let displayName = status.getIn(['account', 'display_name']);
-
-      if (displayName.length === 0) {
-        displayName = status.getIn(['account', 'username']);
-      }
-
-      const displayNameHTML = { __html: emojify(escapeTextContentForBrowser(displayName)) };
-
-      return (
-        <div className='status__wrapper'>
-          <div className='status__prepend'>
-            <div className='status__prepend-icon-wrapper'><i className='fa fa-fw fa-retweet status__prepend-icon' /></div>
-            <FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name: <a onClick={this.handleAccountClick.bind(this, status.getIn(['account', 'id']))} href={status.getIn(['account', 'url'])} className='status__display-name muted'><strong dangerouslySetInnerHTML={displayNameHTML} /></a> }} />
-          </div>
-
-          <Status {...other} wrapped={true} status={status.get('reblog')} />
-        </div>
-      );
-    }
-
-    if (status.get('media_attachments').size > 0 && !this.props.muted) {
-      if (status.get('media_attachments').some(item => item.get('type') === 'unknown')) {
-
-      } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
-        media = <VideoPlayer media={status.getIn(['media_attachments', 0])} sensitive={status.get('sensitive')} onOpenVideo={this.props.onOpenVideo} />;
-      } else {
-        media = <MediaGallery media={status.get('media_attachments')} sensitive={status.get('sensitive')} height={110} onOpenMedia={this.props.onOpenMedia} autoPlayGif={this.props.autoPlayGif} />;
-      }
-    }
-
-    return (
-      <div className={this.props.muted ? 'status muted' : 'status'}>
-        <div className='status__info'>
-          <div className='status__info-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.bind(this, status.getIn(['account', 'id']))} href={status.getIn(['account', 'url'])} className='status__display-name'>
-            <div className='status__avatar'>
-              <Avatar src={status.getIn(['account', 'avatar'])} staticSrc={status.getIn(['account', 'avatar_static'])} size={48} />
-            </div>
-
-            <DisplayName account={status.get('account')} />
-          </a>
-        </div>
-
-        <StatusContent status={status} onClick={this.handleClick} />
-
-        {media}
-
-        <StatusActionBar {...this.props} />
-      </div>
-    );
-  }
-
-}
-
-Status.contextTypes = {
-  router: PropTypes.object
-};
-
-Status.propTypes = {
-  status: ImmutablePropTypes.map,
-  wrapped: PropTypes.bool,
-  onReply: PropTypes.func,
-  onFavourite: PropTypes.func,
-  onReblog: PropTypes.func,
-  onDelete: PropTypes.func,
-  onOpenMedia: PropTypes.func,
-  onOpenVideo: PropTypes.func,
-  onBlock: PropTypes.func,
-  me: PropTypes.number,
-  boostModal: PropTypes.bool,
-  autoPlayGif: PropTypes.bool,
-  muted: PropTypes.bool
-};
-
-export default Status;
diff --git a/app/assets/javascripts/components/components/status_action_bar.jsx b/app/assets/javascripts/components/components/status_action_bar.jsx
deleted file mode 100644
index 29938f23e..000000000
--- a/app/assets/javascripts/components/components/status_action_bar.jsx
+++ /dev/null
@@ -1,137 +0,0 @@
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import IconButton from './icon_button';
-import DropdownMenu from './dropdown_menu';
-import { defineMessages, injectIntl } from 'react-intl';
-
-const messages = defineMessages({
-  delete: { id: 'status.delete', defaultMessage: 'Delete' },
-  mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
-  mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
-  block: { id: 'account.block', defaultMessage: 'Block @{name}' },
-  reply: { id: 'status.reply', defaultMessage: 'Reply' },
-  replyAll: { id: 'status.replyAll', defaultMessage: 'Reply to thread' },
-  reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
-  cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
-  favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
-  open: { id: 'status.open', defaultMessage: 'Expand this status' },
-  report: { id: 'status.report', defaultMessage: 'Report @{name}' }
-});
-
-class StatusActionBar extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.handleReplyClick = this.handleReplyClick.bind(this);
-    this.handleFavouriteClick = this.handleFavouriteClick.bind(this);
-    this.handleReblogClick = this.handleReblogClick.bind(this);
-    this.handleDeleteClick = this.handleDeleteClick.bind(this);
-    this.handleMentionClick = this.handleMentionClick.bind(this);
-    this.handleMuteClick = this.handleMuteClick.bind(this);
-    this.handleBlockClick = this.handleBlockClick.bind(this);
-    this.handleOpen = this.handleOpen.bind(this);
-    this.handleReport = this.handleReport.bind(this);
-  }
-
-  handleReplyClick () {
-    this.props.onReply(this.props.status, this.context.router);
-  }
-
-  handleFavouriteClick () {
-    this.props.onFavourite(this.props.status);
-  }
-
-  handleReblogClick (e) {
-    this.props.onReblog(this.props.status, e);
-  }
-
-  handleDeleteClick () {
-    this.props.onDelete(this.props.status);
-  }
-
-  handleMentionClick () {
-    this.props.onMention(this.props.status.get('account'), this.context.router);
-  }
-
-  handleMuteClick () {
-    this.props.onMute(this.props.status.get('account'));
-  }
-
-  handleBlockClick () {
-    this.props.onBlock(this.props.status.get('account'));
-  }
-
-  handleOpen () {
-    this.context.router.push(`/statuses/${this.props.status.get('id')}`);
-  }
-
-  handleReport () {
-    this.props.onReport(this.props.status);
-    this.context.router.push('/report');
-  }
-
-  render () {
-    const { status, me, intl } = this.props;
-    const reblog_disabled = status.get('visibility') === 'private' || status.get('visibility') === 'direct';
-    let menu = [];
-
-    menu.push({ text: intl.formatMessage(messages.open), action: this.handleOpen });
-    menu.push(null);
-
-    if (status.getIn(['account', 'id']) === me) {
-      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.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick });
-      menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
-      menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
-    }
-
-    let reblogIcon = 'retweet';
-    if (status.get('visibility') === 'direct') reblogIcon = 'envelope';
-    else if (status.get('visibility') === 'private') reblogIcon = 'lock';
-    let reply_icon;
-    let reply_title;
-    if (status.get('in_reply_to_id', null) === null) {
-      reply_icon = "reply";
-      reply_title = intl.formatMessage(messages.reply);
-    } else {
-      reply_icon = "reply-all";
-      reply_title = intl.formatMessage(messages.replyAll);
-    }
-
-    return (
-      <div className='status__action-bar'>
-        <div className='status__action-bar-button-wrapper'><IconButton title={reply_title} icon={reply_icon} onClick={this.handleReplyClick} /></div>
-        <div className='status__action-bar-button-wrapper'><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='status__action-bar-button-wrapper'><IconButton animate={true} active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} className='star-icon' /></div>
-
-        <div className='status__action-bar-dropdown'>
-          <DropdownMenu items={menu} icon='ellipsis-h' size={18} direction="right" ariaLabel="More"/>
-        </div>
-      </div>
-    );
-  }
-
-}
-
-StatusActionBar.contextTypes = {
-  router: PropTypes.object
-};
-
-StatusActionBar.propTypes = {
-  status: ImmutablePropTypes.map.isRequired,
-  onReply: PropTypes.func,
-  onFavourite: PropTypes.func,
-  onReblog: PropTypes.func,
-  onDelete: PropTypes.func,
-  onMention: PropTypes.func,
-  onMute: PropTypes.func,
-  onBlock: PropTypes.func,
-  onReport: PropTypes.func,
-  me: PropTypes.number.isRequired,
-  intl: PropTypes.object.isRequired
-};
-
-export default injectIntl(StatusActionBar);
diff --git a/app/assets/javascripts/components/components/status_content.jsx b/app/assets/javascripts/components/components/status_content.jsx
deleted file mode 100644
index 714c00951..000000000
--- a/app/assets/javascripts/components/components/status_content.jsx
+++ /dev/null
@@ -1,157 +0,0 @@
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import escapeTextContentForBrowser from 'escape-html';
-import PropTypes from 'prop-types';
-import emojify from '../emoji';
-import { isRtl } from '../rtl';
-import { FormattedMessage } from 'react-intl';
-import Permalink from './permalink';
-
-class StatusContent extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.state = {
-      hidden: true
-    };
-    this.onMentionClick = this.onMentionClick.bind(this);
-    this.onHashtagClick = this.onHashtagClick.bind(this);
-    this.handleMouseDown = this.handleMouseDown.bind(this)
-    this.handleMouseUp = this.handleMouseUp.bind(this);
-    this.handleSpoilerClick = this.handleSpoilerClick.bind(this);
-  };
-
-  componentDidMount () {
-    const node  = ReactDOM.findDOMNode(this);
-    const links = node.querySelectorAll('a');
-
-    for (var i = 0; i < links.length; ++i) {
-      let link    = links[i];
-      let mention = this.props.status.get('mentions').find(item => link.href === item.get('url'));
-      let media   = this.props.status.get('media_attachments').find(item => link.href === item.get('text_url') || (item.get('remote_url').length > 0 && link.href === item.get('remote_url')));
-
-      if (mention) {
-        link.addEventListener('click', this.onMentionClick.bind(this, mention), false);
-        link.setAttribute('title', mention.get('acct'));
-      } else if (link.textContent[0] === '#' || (link.previousSibling && link.previousSibling.textContent && link.previousSibling.textContent[link.previousSibling.textContent.length - 1] === '#')) {
-        link.addEventListener('click', this.onHashtagClick.bind(this, link.text), false);
-      } else if (media) {
-        link.innerHTML = '<i class="fa fa-fw fa-photo"></i>';
-      } else {
-        link.setAttribute('target', '_blank');
-        link.setAttribute('rel', 'noopener');
-        link.setAttribute('title', link.href);
-      }
-    }
-  }
-
-  onMentionClick (mention, e) {
-    if (e.button === 0) {
-      e.preventDefault();
-      this.context.router.push(`/accounts/${mention.get('id')}`);
-    }
-  }
-
-  onHashtagClick (hashtag, e) {
-    hashtag = hashtag.replace(/^#/, '').toLowerCase();
-
-    if (e.button === 0) {
-      e.preventDefault();
-      this.context.router.push(`/timelines/tag/${hashtag}`);
-    }
-  }
-
-  handleMouseDown (e) {
-    this.startXY = [e.clientX, e.clientY];
-  }
-
-  handleMouseUp (e) {
-    const [ startX, startY ] = this.startXY;
-    const [ deltaX, deltaY ] = [Math.abs(e.clientX - startX), Math.abs(e.clientY - startY)];
-
-    if (e.target.localName === 'a' || (e.target.parentNode && e.target.parentNode.localName === 'a')) {
-      return;
-    }
-
-    if (deltaX + deltaY < 5 && e.button === 0) {
-      this.props.onClick();
-    }
-
-    this.startXY = null;
-  }
-
-  handleSpoilerClick (e) {
-    e.preventDefault();
-    this.setState({ hidden: !this.state.hidden });
-  }
-
-  render () {
-    const { status } = this.props;
-    const { hidden } = this.state;
-
-    const content = { __html: emojify(status.get('content')) };
-    const spoilerContent = { __html: emojify(escapeTextContentForBrowser(status.get('spoiler_text', ''))) };
-    const directionStyle = { direction: 'ltr' };
-
-    if (isRtl(status.get('content'))) {
-      directionStyle.direction = 'rtl';
-    }
-
-    if (status.get('spoiler_text').length > 0) {
-      let mentionsPlaceholder = '';
-
-      const mentionLinks = status.get('mentions').map(item => (
-        <Permalink to={`/accounts/${item.get('id')}`} href={item.get('url')} key={item.get('id')} className='mention'>
-          @<span>{item.get('username')}</span>
-        </Permalink>
-      )).reduce((aggregate, item) => [...aggregate, item, ' '], [])
-
-      const toggleText = hidden ? <FormattedMessage id='status.show_more' defaultMessage='Show more' /> : <FormattedMessage id='status.show_less' defaultMessage='Show less' />;
-
-      if (hidden) {
-        mentionsPlaceholder = <div>{mentionLinks}</div>;
-      }
-
-      return (
-        <div className='status__content' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
-          <p style={{ marginBottom: hidden && status.get('mentions').size === 0 ? '0px' : '' }} >
-            <span dangerouslySetInnerHTML={spoilerContent} />  <a tabIndex='0' className='status__content__spoiler-link' role='button' onClick={this.handleSpoilerClick}>{toggleText}</a>
-          </p>
-
-          {mentionsPlaceholder}
-
-          <div style={{ display: hidden ? 'none' : 'block', ...directionStyle }} dangerouslySetInnerHTML={content} />
-        </div>
-      );
-    } else if (this.props.onClick) {
-      return (
-        <div
-          className='status__content'
-          style={{ ...directionStyle }}
-          onMouseDown={this.handleMouseDown}
-          onMouseUp={this.handleMouseUp}
-          dangerouslySetInnerHTML={content}
-        />
-      );
-    } else {
-      return (
-        <div
-          className='status__content status__content--no-action'
-          style={{ ...directionStyle }}
-          dangerouslySetInnerHTML={content}
-        />
-      );
-    }
-  }
-
-}
-
-StatusContent.contextTypes = {
-  router: PropTypes.object
-};
-
-StatusContent.propTypes = {
-  status: ImmutablePropTypes.map.isRequired,
-  onClick: PropTypes.func
-};
-
-export default StatusContent;
diff --git a/app/assets/javascripts/components/components/status_list.jsx b/app/assets/javascripts/components/components/status_list.jsx
deleted file mode 100644
index 517c8fe5d..000000000
--- a/app/assets/javascripts/components/components/status_list.jsx
+++ /dev/null
@@ -1,128 +0,0 @@
-import Status from './status';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { ScrollContainer } from 'react-router-scroll';
-import PropTypes from 'prop-types';
-import StatusContainer from '../containers/status_container';
-import LoadMore from './load_more';
-
-class StatusList extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.handleScroll = this.handleScroll.bind(this);
-    this.setRef = this.setRef.bind(this);
-    this.handleLoadMore = this.handleLoadMore.bind(this);
-  }
-
-  handleScroll (e) {
-    const { scrollTop, scrollHeight, clientHeight } = e.target;
-    const offset = scrollHeight - scrollTop - clientHeight;
-    this._oldScrollPosition = scrollHeight - scrollTop;
-
-    if (250 > offset && this.props.onScrollToBottom && !this.props.isLoading) {
-      this.props.onScrollToBottom();
-    } else if (scrollTop < 100 && this.props.onScrollToTop) {
-      this.props.onScrollToTop();
-    } else if (this.props.onScroll) {
-      this.props.onScroll();
-    }
-  }
-
-  componentDidMount () {
-    this.attachScrollListener();
-  }
-
-  componentDidUpdate (prevProps) {
-    if (this.node.scrollTop > 0 && (prevProps.statusIds.size < this.props.statusIds.size && prevProps.statusIds.first() !== this.props.statusIds.first() && !!this._oldScrollPosition)) {
-      this.node.scrollTop = this.node.scrollHeight - this._oldScrollPosition;
-    }
-  }
-
-  componentWillUnmount () {
-    this.detachScrollListener();
-  }
-
-  attachScrollListener () {
-    this.node.addEventListener('scroll', this.handleScroll);
-  }
-
-  detachScrollListener () {
-    this.node.removeEventListener('scroll', this.handleScroll);
-  }
-
-  setRef (c) {
-    this.node = c;
-  }
-
-  handleLoadMore (e) {
-    e.preventDefault();
-    this.props.onScrollToBottom();
-  }
-
-  render () {
-    const { statusIds, onScrollToBottom, scrollKey, shouldUpdateScroll, isLoading, isUnread, hasMore, prepend, emptyMessage } = this.props;
-
-    let loadMore       = '';
-    let scrollableArea = '';
-    let unread         = '';
-
-    if (!isLoading && statusIds.size > 0 && hasMore) {
-      loadMore = <LoadMore onClick={this.handleLoadMore} />;
-    }
-
-    if (isUnread) {
-      unread = <div className='status-list__unread-indicator' />;
-    }
-
-    if (isLoading || statusIds.size > 0 || !emptyMessage) {
-      scrollableArea = (
-        <div className='scrollable' ref={this.setRef}>
-          {unread}
-
-          <div className='status-list'>
-            {prepend}
-
-            {statusIds.map((statusId) => {
-              return <StatusContainer key={statusId} id={statusId} />;
-            })}
-
-            {loadMore}
-          </div>
-        </div>
-      );
-    } else {
-      scrollableArea = (
-        <div className='empty-column-indicator' ref={this.setRef}>
-          {emptyMessage}
-        </div>
-      );
-    }
-
-    return (
-      <ScrollContainer scrollKey={scrollKey} shouldUpdateScroll={shouldUpdateScroll}>
-        {scrollableArea}
-      </ScrollContainer>
-    );
-  }
-
-}
-
-StatusList.propTypes = {
-  scrollKey: PropTypes.string.isRequired,
-  statusIds: ImmutablePropTypes.list.isRequired,
-  onScrollToBottom: PropTypes.func,
-  onScrollToTop: PropTypes.func,
-  onScroll: PropTypes.func,
-  shouldUpdateScroll: PropTypes.func,
-  isLoading: PropTypes.bool,
-  isUnread: PropTypes.bool,
-  hasMore: PropTypes.bool,
-  prepend: PropTypes.node,
-  emptyMessage: PropTypes.node
-};
-
-StatusList.defaultProps = {
-  trackScroll: true
-};
-
-export default StatusList;
diff --git a/app/assets/javascripts/components/components/video_player.jsx b/app/assets/javascripts/components/components/video_player.jsx
deleted file mode 100644
index 09c8ed875..000000000
--- a/app/assets/javascripts/components/components/video_player.jsx
+++ /dev/null
@@ -1,198 +0,0 @@
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import IconButton from './icon_button';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import { isIOS } from '../is_mobile';
-
-const messages = defineMessages({
-  toggle_sound: { id: 'video_player.toggle_sound', defaultMessage: 'Toggle sound' },
-  toggle_visible: { id: 'video_player.toggle_visible', defaultMessage: 'Toggle visibility' },
-  expand_video: { id: 'video_player.expand', defaultMessage: 'Expand video' },
-  expand_video: { id: 'video_player.video_error', defaultMessage: 'Video could not be played' }
-});
-
-class VideoPlayer extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.state = {
-      visible: !this.props.sensitive,
-      preview: true,
-      muted: true,
-      hasAudio: true,
-      videoError: false
-    };
-
-    this.handleClick = this.handleClick.bind(this);
-    this.handleVideoClick = this.handleVideoClick.bind(this);
-    this.handleOpen = this.handleOpen.bind(this);
-    this.handleVisibility = this.handleVisibility.bind(this);
-    this.handleExpand = this.handleExpand.bind(this);
-    this.setRef = this.setRef.bind(this);
-    this.handleLoadedData = this.handleLoadedData.bind(this);
-    this.handleVideoError = this.handleVideoError.bind(this);
-  }
-
-  handleClick () {
-    this.setState({ muted: !this.state.muted });
-  }
-
-  handleVideoClick (e) {
-    e.stopPropagation();
-
-    const node = ReactDOM.findDOMNode(this).querySelector('video');
-
-    if (node.paused) {
-      node.play();
-    } else {
-      node.pause();
-    }
-  }
-
-  handleOpen () {
-    this.setState({ preview: !this.state.preview });
-  }
-
-  handleVisibility () {
-    this.setState({
-      visible: !this.state.visible,
-      preview: true
-    });
-  }
-
-  handleExpand () {
-    this.video.pause();
-    this.props.onOpenVideo(this.props.media, this.video.currentTime);
-  }
-
-  setRef (c) {
-    this.video = c;
-  }
-
-  handleLoadedData () {
-    if (('WebkitAppearance' in document.documentElement.style && this.video.audioTracks.length === 0) || this.video.mozHasAudio === false) {
-      this.setState({ hasAudio: false });
-    }
-  }
-
-  handleVideoError () {
-    this.setState({ videoError: true });
-  }
-
-  componentDidMount () {
-    if (!this.video) {
-      return;
-    }
-
-    this.video.addEventListener('loadeddata', this.handleLoadedData);
-    this.video.addEventListener('error', this.handleVideoError);
-  }
-
-  componentDidUpdate () {
-    if (!this.video) {
-      return;
-    }
-
-    this.video.addEventListener('loadeddata', this.handleLoadedData);
-    this.video.addEventListener('error', this.handleVideoError);
-  }
-
-  componentWillUnmount () {
-    if (!this.video) {
-      return;
-    }
-
-    this.video.removeEventListener('loadeddata', this.handleLoadedData);
-    this.video.removeEventListener('error', this.handleVideoError);
-  }
-
-  render () {
-    const { media, intl, width, height, sensitive, autoplay } = this.props;
-
-    let spoilerButton = (
-      <div className='status__video-player-spoiler' style={{ display: !this.state.visible ? 'none' : 'block' }} >
-        <IconButton overlay title={intl.formatMessage(messages.toggle_visible)} icon={this.state.visible ? 'eye' : 'eye-slash'} onClick={this.handleVisibility} />
-      </div>
-    );
-
-    let expandButton = (
-      <div className='status__video-player-expand'>
-        <IconButton overlay title={intl.formatMessage(messages.expand_video)} icon='expand' onClick={this.handleExpand} />
-      </div>
-    );
-
-    let muteButton = '';
-
-    if (this.state.hasAudio) {
-      muteButton = (
-        <div className='status__video-player-mute'>
-          <IconButton overlay title={intl.formatMessage(messages.toggle_sound)} icon={this.state.muted ? 'volume-off' : 'volume-up'} onClick={this.handleClick} />
-        </div>
-      );
-    }
-
-    if (!this.state.visible) {
-      if (sensitive) {
-        return (
-          <div role='button' tabIndex='0' style={{ width: `${width}px`, height: `${height}px` }} className='media-spoiler' onClick={this.handleVisibility}>
-            {spoilerButton}
-            <span className='media-spoiler__warning'><FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' /></span>
-            <span className='media-spoiler__trigger'><FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' /></span>
-          </div>
-        );
-      } else {
-        return (
-          <div role='button' tabIndex='0' style={{ width: `${width}px`, height: `${height}px` }} className='media-spoiler' onClick={this.handleVisibility}>
-            {spoilerButton}
-            <span className='media-spoiler__warning'><FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' /></span>
-            <span className='media-spoiler__trigger'><FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' /></span>
-          </div>
-        );
-      }
-    }
-
-    if (this.state.preview && !autoplay) {
-      return (
-        <div role='button' tabIndex='0' className='media-spoiler-video' style={{ width: `${width}px`, height: `${height}px`, background: `url(${media.get('preview_url')}) no-repeat center` }} onClick={this.handleOpen}>
-          {spoilerButton}
-          <div className='media-spoiler-video-play-icon'><i className='fa fa-play' /></div>
-        </div>
-      );
-    }
-
-    if (this.state.videoError) {
-      return (
-        <div style={{ width: `${width}px`, height: `${height}px` }} className='video-error-cover' >
-          <span className='media-spoiler__warning'><FormattedMessage id='video_player.video_error' defaultMessage='Video could not be played' /></span>
-        </div>
-      );
-    }
-
-    return (
-      <div className='status__video-player' style={{ width: `${width}px`, height: `${height}px` }}>
-        {spoilerButton}
-        {muteButton}
-        {expandButton}
-        <video className='status__video-player-video' role='button' tabIndex='0' ref={this.setRef} src={media.get('url')} autoPlay={!isIOS()} loop={true} muted={this.state.muted} onClick={this.handleVideoClick} />
-      </div>
-    );
-  }
-
-}
-
-VideoPlayer.propTypes = {
-  media: ImmutablePropTypes.map.isRequired,
-  width: PropTypes.number,
-  height: PropTypes.number,
-  sensitive: PropTypes.bool,
-  intl: PropTypes.object.isRequired,
-  autoplay: PropTypes.bool,
-  onOpenVideo: PropTypes.func.isRequired
-};
-
-VideoPlayer.defaultProps = {
-  width: 239,
-  height: 110
-};
-
-export default injectIntl(VideoPlayer);