about summary refs log tree commit diff
path: root/app/assets/javascripts/components/features/ui
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/components/features/ui')
-rw-r--r--app/assets/javascripts/components/features/ui/components/boost_modal.jsx82
-rw-r--r--app/assets/javascripts/components/features/ui/components/column.jsx82
-rw-r--r--app/assets/javascripts/components/features/ui/components/column_header.jsx42
-rw-r--r--app/assets/javascripts/components/features/ui/components/column_link.jsx31
-rw-r--r--app/assets/javascripts/components/features/ui/components/column_subheading.jsx15
-rw-r--r--app/assets/javascripts/components/features/ui/components/columns_area.jsx19
-rw-r--r--app/assets/javascripts/components/features/ui/components/confirmation_modal.jsx50
-rw-r--r--app/assets/javascripts/components/features/ui/components/media_modal.jsx101
-rw-r--r--app/assets/javascripts/components/features/ui/components/modal_root.jsx92
-rw-r--r--app/assets/javascripts/components/features/ui/components/onboarding_modal.jsx263
-rw-r--r--app/assets/javascripts/components/features/ui/components/tabs_bar.jsx23
-rw-r--r--app/assets/javascripts/components/features/ui/components/upload_area.jsx59
-rw-r--r--app/assets/javascripts/components/features/ui/components/video_modal.jsx38
-rw-r--r--app/assets/javascripts/components/features/ui/containers/loading_bar_container.jsx8
-rw-r--r--app/assets/javascripts/components/features/ui/containers/modal_container.jsx16
-rw-r--r--app/assets/javascripts/components/features/ui/containers/notifications_container.jsx21
-rw-r--r--app/assets/javascripts/components/features/ui/containers/status_list_container.jsx74
-rw-r--r--app/assets/javascripts/components/features/ui/index.jsx166
18 files changed, 0 insertions, 1182 deletions
diff --git a/app/assets/javascripts/components/features/ui/components/boost_modal.jsx b/app/assets/javascripts/components/features/ui/components/boost_modal.jsx
deleted file mode 100644
index 3bd82ceee..000000000
--- a/app/assets/javascripts/components/features/ui/components/boost_modal.jsx
+++ /dev/null
@@ -1,82 +0,0 @@
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import IconButton from '../../../components/icon_button';
-import Button from '../../../components/button';
-import StatusContent from '../../../components/status_content';
-import Avatar from '../../../components/avatar';
-import RelativeTimestamp from '../../../components/relative_timestamp';
-import DisplayName from '../../../components/display_name';
-
-const messages = defineMessages({
-  reblog: { id: 'status.reblog', defaultMessage: 'Boost' }
-});
-
-class BoostModal extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.handleReblog = this.handleReblog.bind(this);
-    this.handleAccountClick = this.handleAccountClick.bind(this);
-  }
-
-  handleReblog() {
-    this.props.onReblog(this.props.status);
-    this.props.onClose();
-  }
-
-  handleAccountClick (e) {
-    if (e.button === 0) {
-      e.preventDefault();
-      this.props.onClose();
-      this.context.router.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
-    }
-  }
-
-  render () {
-    const { status, intl, onClose } = 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 src={status.getIn(['account', 'avatar'])} staticSrc={status.getIn(['account', 'avatar_static'])} 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} />
-        </div>
-      </div>
-    );
-  }
-
-}
-
-BoostModal.contextTypes = {
-  router: PropTypes.object
-};
-
-BoostModal.propTypes = {
-  status: ImmutablePropTypes.map.isRequired,
-  onReblog: PropTypes.func.isRequired,
-  onClose: PropTypes.func.isRequired,
-  intl: PropTypes.object.isRequired
-};
-
-export default injectIntl(BoostModal);
diff --git a/app/assets/javascripts/components/features/ui/components/column.jsx b/app/assets/javascripts/components/features/ui/components/column.jsx
deleted file mode 100644
index aa09d0fd2..000000000
--- a/app/assets/javascripts/components/features/ui/components/column.jsx
+++ /dev/null
@@ -1,82 +0,0 @@
-import ColumnHeader from './column_header';
-import PropTypes from 'prop-types';
-
-const easingOutQuint = (x, t, b, c, d) => c*((t=t/d-1)*t*t*t*t + 1) + b;
-
-const scrollTop = (node) => {
-  const startTime = Date.now();
-  const offset    = node.scrollTop;
-  const targetY   = -offset;
-  const duration  = 1000;
-  let interrupt   = false;
-
-  const step = () => {
-    const elapsed    = Date.now() - startTime;
-    const percentage = elapsed / duration;
-
-    if (percentage > 1 || interrupt) {
-      return;
-    }
-
-    node.scrollTop = easingOutQuint(0, elapsed, offset, targetY, duration);
-    requestAnimationFrame(step);
-  };
-
-  step();
-
-  return () => {
-    interrupt = true;
-  };
-};
-
-class Column extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.handleHeaderClick = this.handleHeaderClick.bind(this);
-    this.handleWheel = this.handleWheel.bind(this);
-  }
-
-  handleHeaderClick () {
-    const scrollable = ReactDOM.findDOMNode(this).querySelector('.scrollable');
-    if (!scrollable) {
-      return;
-    }
-    this._interruptScrollAnimation = scrollTop(scrollable);
-  }
-
-  handleWheel () {
-    if (typeof this._interruptScrollAnimation !== 'undefined') {
-      this._interruptScrollAnimation();
-    }
-  }
-
-  render () {
-    const { heading, icon, children, active, hideHeadingOnMobile } = this.props;
-
-    let columnHeaderId = null
-    let header = '';
-
-    if (heading) {
-      columnHeaderId = heading.replace(/ /g, '-')
-      header = <ColumnHeader icon={icon} active={active} type={heading} onClick={this.handleHeaderClick} hideOnMobile={hideHeadingOnMobile} columnHeaderId={columnHeaderId}/>;
-    }
-    return (
-      <div role='region' aria-labelledby={columnHeaderId} className='column' onWheel={this.handleWheel}>
-        {header}
-        {children}
-      </div>
-    );
-  }
-
-}
-
-Column.propTypes = {
-  heading: PropTypes.string,
-  icon: PropTypes.string,
-  children: PropTypes.node,
-  active: PropTypes.bool,
-  hideHeadingOnMobile: PropTypes.bool
-};
-
-export default Column;
diff --git a/app/assets/javascripts/components/features/ui/components/column_header.jsx b/app/assets/javascripts/components/features/ui/components/column_header.jsx
deleted file mode 100644
index 7ccd72e0b..000000000
--- a/app/assets/javascripts/components/features/ui/components/column_header.jsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import PropTypes from 'prop-types'
-
-class ColumnHeader extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.handleClick = this.handleClick.bind(this);
-  }
-
-  handleClick () {
-    this.props.onClick();
-  }
-
-  render () {
-    const { type, active, hideOnMobile, 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='button heading' tabIndex='0' className={`column-header ${active ? 'active' : ''} ${hideOnMobile ? 'hidden-on-mobile' : ''}`} onClick={this.handleClick} id={columnHeaderId || null}>
-        {icon}
-        {type}
-      </div>
-    );
-  }
-
-}
-
-ColumnHeader.propTypes = {
-  icon: PropTypes.string,
-  type: PropTypes.string,
-  active: PropTypes.bool,
-  onClick: PropTypes.func,
-  hideOnMobile: PropTypes.bool,
-  columnHeaderId: PropTypes.string
-};
-
-export default ColumnHeader;
diff --git a/app/assets/javascripts/components/features/ui/components/column_link.jsx b/app/assets/javascripts/components/features/ui/components/column_link.jsx
deleted file mode 100644
index 820e4246a..000000000
--- a/app/assets/javascripts/components/features/ui/components/column_link.jsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import PropTypes from 'prop-types';
-import { Link } from 'react-router';
-
-const ColumnLink = ({ icon, text, to, href, method, hideOnMobile }) => {
-  if (href) {
-    return (
-      <a href={href} className={`column-link ${hideOnMobile ? 'hidden-on-mobile' : ''}`} data-method={method}>
-        <i className={`fa fa-fw fa-${icon} column-link__icon`} />
-        {text}
-      </a>
-    );
-  } else {
-    return (
-      <Link to={to} className={`column-link ${hideOnMobile ? 'hidden-on-mobile' : ''}`}>
-        <i className={`fa fa-fw fa-${icon} column-link__icon`} />
-        {text}
-      </Link>
-    );
-  }
-};
-
-ColumnLink.propTypes = {
-  icon: PropTypes.string.isRequired,
-  text: PropTypes.string.isRequired,
-  to: PropTypes.string,
-  href: PropTypes.string,
-  method: PropTypes.string,
-  hideOnMobile: PropTypes.bool
-};
-
-export default ColumnLink;
diff --git a/app/assets/javascripts/components/features/ui/components/column_subheading.jsx b/app/assets/javascripts/components/features/ui/components/column_subheading.jsx
deleted file mode 100644
index 061c8be6c..000000000
--- a/app/assets/javascripts/components/features/ui/components/column_subheading.jsx
+++ /dev/null
@@ -1,15 +0,0 @@
-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/assets/javascripts/components/features/ui/components/columns_area.jsx b/app/assets/javascripts/components/features/ui/components/columns_area.jsx
deleted file mode 100644
index 360a759ae..000000000
--- a/app/assets/javascripts/components/features/ui/components/columns_area.jsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import PropTypes from 'prop-types';
-
-class ColumnsArea extends React.PureComponent {
-
-  render () {
-    return (
-      <div className='columns-area'>
-        {this.props.children}
-      </div>
-    );
-  }
-
-}
-
-ColumnsArea.propTypes = {
-  children: PropTypes.node
-};
-
-export default ColumnsArea;
diff --git a/app/assets/javascripts/components/features/ui/components/confirmation_modal.jsx b/app/assets/javascripts/components/features/ui/components/confirmation_modal.jsx
deleted file mode 100644
index 914c12f82..000000000
--- a/app/assets/javascripts/components/features/ui/components/confirmation_modal.jsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import Button from '../../../components/button';
-
-class ConfirmationModal extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.handleClick = this.handleClick.bind(this);
-    this.handleCancel = this.handleCancel.bind(this);
-  }
-
-  handleClick () {
-    this.props.onClose();
-    this.props.onConfirm();
-  }
-
-  handleCancel (e) {
-    e.preventDefault();
-    this.props.onClose();
-  }
-
-  render () {
-    const { intl, message, confirm, onConfirm, onClose } = this.props;
-
-    return (
-      <div className='modal-root__modal confirmation-modal'>
-        <div className='confirmation-modal__container'>
-          {message}
-        </div>
-
-        <div className='confirmation-modal__action-bar'>
-          <div><a href='#' onClick={this.handleCancel}><FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' /></a></div>
-          <Button text={confirm} onClick={this.handleClick} />
-        </div>
-      </div>
-    );
-  }
-
-}
-
-ConfirmationModal.propTypes = {
-  message: PropTypes.node.isRequired,
-  confirm: PropTypes.string.isRequired,
-  onClose: PropTypes.func.isRequired,
-  onConfirm: PropTypes.func.isRequired,
-  intl: PropTypes.object.isRequired
-};
-
-export default injectIntl(ConfirmationModal);
diff --git a/app/assets/javascripts/components/features/ui/components/media_modal.jsx b/app/assets/javascripts/components/features/ui/components/media_modal.jsx
deleted file mode 100644
index 02a577500..000000000
--- a/app/assets/javascripts/components/features/ui/components/media_modal.jsx
+++ /dev/null
@@ -1,101 +0,0 @@
-import LoadingIndicator from '../../../components/loading_indicator';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import ExtendedVideoPlayer from '../../../components/extended_video_player';
-import ImageLoader from 'react-imageloader';
-import { defineMessages, injectIntl } from 'react-intl';
-import IconButton from '../../../components/icon_button';
-
-const messages = defineMessages({
-  close: { id: 'lightbox.close', defaultMessage: 'Close' }
-});
-
-class MediaModal extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.state = {
-      index: null
-    };
-    this.handleNextClick = this.handleNextClick.bind(this);
-    this.handlePrevClick = this.handlePrevClick.bind(this);
-    this.handleKeyUp = this.handleKeyUp.bind(this);
-  }
-
-  handleNextClick () {
-    this.setState({ index: (this.getIndex() + 1) % this.props.media.size});
-  }
-
-  handlePrevClick () {
-    this.setState({ index: (this.getIndex() - 1) % 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();
-    const attachment = media.get(index);
-    const url = attachment.get('url');
-
-    let leftNav, rightNav, content;
-
-    leftNav = rightNav = content = '';
-
-    if (media.size > 1) {
-      leftNav  = <div role='button' tabIndex='0' className='modal-container__nav modal-container__nav--left' onClick={this.handlePrevClick}><i className='fa fa-fw fa-chevron-left' /></div>;
-      rightNav = <div role='button' tabIndex='0' className='modal-container__nav  modal-container__nav--right' onClick={this.handleNextClick}><i className='fa fa-fw fa-chevron-right' /></div>;
-    }
-
-    if (attachment.get('type') === 'image') {
-      content = <ImageLoader src={url} imgProps={{ style: { display: 'block' } }} />;
-    } else if (attachment.get('type') === 'gifv') {
-      content = <ExtendedVideoPlayer src={url} muted={true} controls={false} />;
-    }
-
-    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} />
-          {content}
-        </div>
-
-        {rightNav}
-      </div>
-    );
-  }
-
-}
-
-MediaModal.propTypes = {
-  media: ImmutablePropTypes.list.isRequired,
-  index: PropTypes.number.isRequired,
-  onClose: PropTypes.func.isRequired,
-  intl: PropTypes.object.isRequired
-};
-
-export default injectIntl(MediaModal);
diff --git a/app/assets/javascripts/components/features/ui/components/modal_root.jsx b/app/assets/javascripts/components/features/ui/components/modal_root.jsx
deleted file mode 100644
index 23057715c..000000000
--- a/app/assets/javascripts/components/features/ui/components/modal_root.jsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import PropTypes from 'prop-types';
-import MediaModal from './media_modal';
-import OnboardingModal from './onboarding_modal';
-import VideoModal from './video_modal';
-import BoostModal from './boost_modal';
-import ConfirmationModal from './confirmation_modal';
-import { TransitionMotion, spring } from 'react-motion';
-
-const MODAL_COMPONENTS = {
-  'MEDIA': MediaModal,
-  'ONBOARDING': OnboardingModal,
-  'VIDEO': VideoModal,
-  'BOOST': BoostModal,
-  'CONFIRM': ConfirmationModal
-};
-
-class ModalRoot extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.handleKeyUp = this.handleKeyUp.bind(this);
-  }
-
-  handleKeyUp (e) {
-    if ((e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27)
-         && !!this.props.type) {
-      this.props.onClose();
-    }
-  }
-
-  componentDidMount () {
-    window.addEventListener('keyup', this.handleKeyUp, false);
-  }
-
-  componentWillUnmount () {
-    window.removeEventListener('keyup', this.handleKeyUp);
-  }
-
-  willEnter () {
-    return { opacity: 0, scale: 0.98 };
-  }
-
-  willLeave () {
-    return { opacity: spring(0), scale: spring(0.98) };
-  }
-
-  render () {
-    const { type, props, onClose } = this.props;
-    const items = [];
-
-    if (!!type) {
-      items.push({
-        key: type,
-        data: { type, props },
-        style: { opacity: spring(1), scale: spring(1, { stiffness: 120, damping: 14 }) }
-      });
-    }
-
-    return (
-      <TransitionMotion
-        styles={items}
-        willEnter={this.willEnter}
-        willLeave={this.willLeave}>
-        {interpolatedStyles =>
-          <div className='modal-root'>
-            {interpolatedStyles.map(({ key, data: { type, props }, style }) => {
-              const SpecificComponent = MODAL_COMPONENTS[type];
-
-              return (
-                <div key={key}>
-                  <div role='presentation' className='modal-root__overlay' style={{ opacity: style.opacity }} onClick={onClose} />
-                  <div className='modal-root__container' style={{ opacity: style.opacity, transform: `translateZ(0px) scale(${style.scale})` }}>
-                    <SpecificComponent {...props} onClose={onClose} />
-                  </div>
-                </div>
-              );
-            })}
-          </div>
-        }
-      </TransitionMotion>
-    );
-  }
-
-}
-
-ModalRoot.propTypes = {
-  type: PropTypes.string,
-  props: PropTypes.object,
-  onClose: PropTypes.func.isRequired
-};
-
-export default ModalRoot;
diff --git a/app/assets/javascripts/components/features/ui/components/onboarding_modal.jsx b/app/assets/javascripts/components/features/ui/components/onboarding_modal.jsx
deleted file mode 100644
index 4c2c55f93..000000000
--- a/app/assets/javascripts/components/features/ui/components/onboarding_modal.jsx
+++ /dev/null
@@ -1,263 +0,0 @@
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import Permalink from '../../../components/permalink';
-import { TransitionMotion, spring } from 'react-motion';
-import ComposeForm from '../../compose/components/compose_form';
-import Search from '../../compose/components/search';
-import NavigationBar from '../../compose/components/navigation_bar';
-import ColumnHeader from './column_header';
-import Immutable from 'immutable';
-
-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 Mastodon!' /></h1>
-      <p><FormattedMessage id='onboarding.page_one.federation' defaultMessage='Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.' /></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 = ({ me }) => (
-  <div className='onboarding-modal__page onboarding-modal__page-two'>
-    <div className='figure non-interactive'>
-      <div className='pseudo-drawer'>
-        <NavigationBar account={me} />
-      </div>
-      <ComposeForm
-        text='Awoo! #introductions'
-        suggestions={Immutable.List()}
-        mentionedDomains={[]}
-        spoiler={false}
-        onChange={() => {}}
-        onSubmit={() => {}}
-        onPaste={() => {}}
-        onPickEmoji={() => {}}
-        onChangeSpoilerText={() => {}}
-        onClearSuggestions={() => {}}
-        onFetchSuggestions={() => {}}
-        onSuggestionSelected={() => {}}
-      />
-    </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 = {
-  me: ImmutablePropTypes.map.isRequired,
-};
-
-const PageThree = ({ me, domain }) => (
-  <div className='onboarding-modal__page onboarding-modal__page-three'>
-    <div className='figure non-interactive'>
-      <Search
-        value=''
-        onChange={() => {}}
-        onSubmit={() => {}}
-        onClear={() => {}}
-        onShow={() => {}}
-      />
-
-      <div className='pseudo-drawer'>
-        <NavigationBar account={me} />
-      </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 = {
-  me: ImmutablePropTypes.map.isRequired,
-  domain: PropTypes.string.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='Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.' values={{ github: <a href='https://github.com/tootsuite/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={{ 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 => ({
-  me: state.getIn(['accounts', state.getIn(['meta', 'me'])]),
-  admin: state.getIn(['accounts', state.getIn(['meta', 'admin'])]),
-  domain: state.getIn(['meta', 'domain'])
-});
-
-class OnboardingModal extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.state = {
-      currentIndex: 0
-    };
-    this.handleSkip = this.handleSkip.bind(this);
-    this.handleDot = this.handleDot.bind(this);
-    this.handleNext = this.handleNext.bind(this);
-  }
-
-  handleSkip (e) {
-    e.preventDefault();
-    this.props.onClose();
-  }
-
-  handleDot (i, e) {
-    e.preventDefault();
-    this.setState({ currentIndex: i });
-  }
-
-  handleNext (maxNum, e) {
-    e.preventDefault();
-
-    if (this.state.currentIndex < maxNum - 1) {
-      this.setState({ currentIndex: this.state.currentIndex + 1 });
-    } else {
-      this.props.onClose();
-    }
-  }
-
-  render () {
-    const { me, admin, domain, intl } = this.props;
-
-    const pages = [
-      <PageOne acct={me.get('acct')} domain={domain} />,
-      <PageTwo me={me} />,
-      <PageThree me={me} domain={domain} />,
-      <PageFour domain={domain} intl={intl} />,
-      <PageSix admin={admin} domain={domain} />
-    ];
-
-    const { currentIndex } = this.state;
-    const hasMore = currentIndex < pages.length - 1;
-
-    let nextOrDoneBtn;
-
-    if(hasMore) {
-      nextOrDoneBtn = <a href='#' onClick={this.handleNext.bind(null, pages.length)} className='onboarding-modal__nav onboarding-modal__next'><FormattedMessage id='onboarding.next' defaultMessage='Next' /></a>;
-    } else {
-      nextOrDoneBtn = <a href='#' onClick={this.handleNext.bind(null, pages.length)} className='onboarding-modal__nav onboarding-modal__done'><FormattedMessage id='onboarding.done' defaultMessage='Done' /></a>;
-    }
-
-    const styles = pages.map((page, i) => ({
-      key: `page-${i}`,
-      style: { opacity: spring(i === currentIndex ? 1 : 0) }
-    }));
-
-    return (
-      <div className='modal-root__modal onboarding-modal'>
-        <TransitionMotion styles={styles}>
-          {interpolatedStyles =>
-            <div className='onboarding-modal__pager'>
-              {pages.map((page, i) =>
-                <div key={`page-${i}`} style={{ opacity: interpolatedStyles[i].style.opacity, pointerEvents: i === currentIndex ? 'auto' : 'none' }}>{page}</div>
-              )}
-            </div>
-          }
-        </TransitionMotion>
-
-        <div className='onboarding-modal__paginator'>
-          <div>
-            <a href='#' className='onboarding-modal__skip' onClick={this.handleSkip}><FormattedMessage id='onboarding.skip' defaultMessage='Skip' /></a>
-          </div>
-
-          <div className='onboarding-modal__dots'>
-            {pages.map((_, i) => <div key={i} onClick={this.handleDot.bind(null, i)} className={`onboarding-modal__dot ${i === currentIndex ? 'active' : ''}`} />)}
-          </div>
-
-          <div>
-            {nextOrDoneBtn}
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-}
-
-OnboardingModal.propTypes = {
-  onClose: PropTypes.func.isRequired,
-  intl: PropTypes.object.isRequired,
-  me: ImmutablePropTypes.map.isRequired,
-  domain: PropTypes.string.isRequired,
-  admin: ImmutablePropTypes.map
-}
-
-export default connect(mapStateToProps)(injectIntl(OnboardingModal));
diff --git a/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx b/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx
deleted file mode 100644
index 316b4bf7d..000000000
--- a/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { Link } from 'react-router';
-import { FormattedMessage } from 'react-intl';
-
-class TabsBar extends React.Component {
-
-  render () {
-    return (
-      <div className='tabs-bar'>
-        <Link className='tabs-bar__link primary' activeClassName='active' to='/statuses/new'><i className='fa fa-fw fa-pencil' /><FormattedMessage id='tabs_bar.compose' defaultMessage='Compose' /></Link>
-        <Link className='tabs-bar__link primary' activeClassName='active' to='/timelines/home'><i className='fa fa-fw fa-home' /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></Link>
-        <Link className='tabs-bar__link primary' activeClassName='active' to='/notifications'><i className='fa fa-fw fa-bell' /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></Link>
-
-        <Link className='tabs-bar__link secondary' activeClassName='active' to='/timelines/public/local'><i className='fa fa-fw fa-users' /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></Link>
-        <Link className='tabs-bar__link secondary' activeClassName='active' to='/timelines/public'><i className='fa fa-fw fa-globe' /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></Link>
-
-        <Link className='tabs-bar__link primary' activeClassName='active' style={{ flexGrow: '0', flexBasis: '30px' }} to='/getting-started'><i className='fa fa-fw fa-asterisk' /></Link>
-      </div>
-    );
-  }
-
-}
-
-export default TabsBar;
diff --git a/app/assets/javascripts/components/features/ui/components/upload_area.jsx b/app/assets/javascripts/components/features/ui/components/upload_area.jsx
deleted file mode 100644
index 3a933398b..000000000
--- a/app/assets/javascripts/components/features/ui/components/upload_area.jsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import PropTypes from 'prop-types';
-import { Motion, spring } from 'react-motion';
-import { FormattedMessage } from 'react-intl';
-
-class UploadArea extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-
-    this.handleKeyUp = this.handleKeyUp.bind(this);
-  }
-
-  handleKeyUp (e) {
-    e.preventDefault();
-    e.stopPropagation();
-
-    const keyCode = e.keyCode
-    if (this.props.active) {
-      switch(keyCode) {
-      case 27:
-        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: `translateZ(0) scale(${backgroundScale})` }} />
-              <div className='upload-area__content'><FormattedMessage id='upload_area.title' defaultMessage='Drag & drop to upload' /></div>
-            </div>
-          </div>
-        }
-      </Motion>
-    );
-  }
-
-}
-
-UploadArea.propTypes = {
-  active: PropTypes.bool,
-  onClose: PropTypes.func
-};
-
-export default UploadArea;
diff --git a/app/assets/javascripts/components/features/ui/components/video_modal.jsx b/app/assets/javascripts/components/features/ui/components/video_modal.jsx
deleted file mode 100644
index d98b42882..000000000
--- a/app/assets/javascripts/components/features/ui/components/video_modal.jsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import LoadingIndicator from '../../../components/loading_indicator';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import PropTypes from 'prop-types';
-import ExtendedVideoPlayer from '../../../components/extended_video_player';
-import { defineMessages, injectIntl } from 'react-intl';
-import IconButton from '../../../components/icon_button';
-
-const messages = defineMessages({
-  close: { id: 'lightbox.close', defaultMessage: 'Close' }
-});
-
-class VideoModal extends React.PureComponent {
-
-  render () {
-    const { media, intl, time, onClose } = this.props;
-
-    const url = media.get('url');
-
-    return (
-      <div className='modal-root__modal media-modal'>
-        <div>
-          <div className='media-modal__close'><IconButton title={intl.formatMessage(messages.close)} icon='times' overlay onClick={onClose} /></div>
-          <ExtendedVideoPlayer src={url} muted={false} controls={true} time={time} />
-        </div>
-      </div>
-    );
-  }
-
-}
-
-VideoModal.propTypes = {
-  media: ImmutablePropTypes.map.isRequired,
-  time: PropTypes.number,
-  onClose: PropTypes.func.isRequired,
-  intl: PropTypes.object.isRequired
-};
-
-export default injectIntl(VideoModal);
diff --git a/app/assets/javascripts/components/features/ui/containers/loading_bar_container.jsx b/app/assets/javascripts/components/features/ui/containers/loading_bar_container.jsx
deleted file mode 100644
index 6c4e73e38..000000000
--- a/app/assets/javascripts/components/features/ui/containers/loading_bar_container.jsx
+++ /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/assets/javascripts/components/features/ui/containers/modal_container.jsx b/app/assets/javascripts/components/features/ui/containers/modal_container.jsx
deleted file mode 100644
index 26d77818c..000000000
--- a/app/assets/javascripts/components/features/ui/containers/modal_container.jsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { connect } from 'react-redux';
-import { closeModal } from '../../../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/assets/javascripts/components/features/ui/containers/notifications_container.jsx b/app/assets/javascripts/components/features/ui/containers/notifications_container.jsx
deleted file mode 100644
index 529ebf6c8..000000000
--- a/app/assets/javascripts/components/features/ui/containers/notifications_container.jsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import { connect } from 'react-redux';
-import { NotificationStack } from 'react-notification';
-import {
-  dismissAlert,
-  clearAlerts
-} from '../../../actions/alerts';
-import { getAlerts } from '../../../selectors';
-
-const mapStateToProps = (state, props) => ({
-  notifications: getAlerts(state)
-});
-
-const mapDispatchToProps = (dispatch) => {
-  return {
-    onDismiss: alert => {
-      dispatch(dismissAlert(alert));
-    }
-  };
-};
-
-export default connect(mapStateToProps, mapDispatchToProps)(NotificationStack);
diff --git a/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx b/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx
deleted file mode 100644
index 1599000b5..000000000
--- a/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx
+++ /dev/null
@@ -1,74 +0,0 @@
-import { connect } from 'react-redux';
-import StatusList from '../../../components/status_list';
-import { expandTimeline, scrollTopTimeline } from '../../../actions/timelines';
-import Immutable from 'immutable';
-import { createSelector } from 'reselect';
-import { debounce } from 'react-decoration';
-
-const makeGetStatusIds = () => createSelector([
-  (state, { type }) => state.getIn(['settings', type], Immutable.Map()),
-  (state, { type }) => state.getIn(['timelines', type, 'items'], Immutable.List()),
-  (state)           => state.get('statuses'),
-  (state)           => state.getIn(['meta', 'me'])
-], (columnSettings, statusIds, statuses, me) => 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 (columnSettings.getIn(['regex', 'body'], '').trim().length > 0) {
-    try {
-      if (showStatus) {
-        const regex = new RegExp(columnSettings.getIn(['regex', 'body']).trim(), 'i');
-        showStatus = !regex.test(statusForId.get('reblog') ? statuses.getIn([statusForId.get('reblog'), 'unescaped_content']) : statusForId.get('unescaped_content'));
-      }
-    } catch(e) {
-      // Bad regex, don't affect filters
-    }
-  }
-
-  return showStatus;
-}));
-
-const makeMapStateToProps = () => {
-  const getStatusIds = makeGetStatusIds();
-
-  const mapStateToProps = (state, props) => ({
-    scrollKey: props.scrollKey,
-    shouldUpdateScroll: props.shouldUpdateScroll,
-    statusIds: getStatusIds(state, props),
-    isLoading: state.getIn(['timelines', props.type, 'isLoading'], true),
-    isUnread: state.getIn(['timelines', props.type, 'unread']) > 0,
-    hasMore: !!state.getIn(['timelines', props.type, 'next'])
-  });
-
-  return mapStateToProps;
-};
-
-const mapDispatchToProps = (dispatch, { type, id }) => ({
-
-  @debounce(300, true)
-  onScrollToBottom () {
-    dispatch(scrollTopTimeline(type, false));
-    dispatch(expandTimeline(type, id));
-  },
-
-  @debounce(100)
-  onScrollToTop () {
-    dispatch(scrollTopTimeline(type, true));
-  },
-
-  @debounce(100)
-  onScroll () {
-    dispatch(scrollTopTimeline(type, false));
-  }
-
-});
-
-export default connect(makeMapStateToProps, mapDispatchToProps)(StatusList);
diff --git a/app/assets/javascripts/components/features/ui/index.jsx b/app/assets/javascripts/components/features/ui/index.jsx
deleted file mode 100644
index b402639ce..000000000
--- a/app/assets/javascripts/components/features/ui/index.jsx
+++ /dev/null
@@ -1,166 +0,0 @@
-import ColumnsArea from './components/columns_area';
-import NotificationsContainer from './containers/notifications_container';
-import PropTypes from 'prop-types';
-import LoadingBarContainer from './containers/loading_bar_container';
-import HomeTimeline from '../home_timeline';
-import Compose from '../compose';
-import TabsBar from './components/tabs_bar';
-import ModalContainer from './containers/modal_container';
-import Notifications from '../notifications';
-import { connect } from 'react-redux';
-import { isMobile } from '../../is_mobile';
-import { debounce } from 'react-decoration';
-import { uploadCompose } from '../../actions/compose';
-import { refreshTimeline } from '../../actions/timelines';
-import { refreshNotifications } from '../../actions/notifications';
-import UploadArea from './components/upload_area';
-
-class UI extends React.PureComponent {
-
-  constructor (props, context) {
-    super(props, context);
-    this.state = {
-      width: window.innerWidth,
-      draggingOver: false
-    };
-    this.handleResize = this.handleResize.bind(this);
-    this.handleDragEnter = this.handleDragEnter.bind(this);
-    this.handleDragOver = this.handleDragOver.bind(this);
-    this.handleDrop = this.handleDrop.bind(this);
-    this.handleDragLeave = this.handleDragLeave.bind(this);
-    this.handleDragEnd = this.handleDragLeave.bind(this)
-    this.closeUploadModal = this.closeUploadModal.bind(this)
-    this.setRef = this.setRef.bind(this);
-  }
-
-  @debounce(500)
-  handleResize () {
-    this.setState({ width: window.innerWidth });
-  }
-
-  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 });
-  }
-
-  componentWillMount () {
-    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);
-
-    this.props.dispatch(refreshTimeline('home'));
-    this.props.dispatch(refreshNotifications());
-  }
-
-  componentWillUnmount () {
-    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;
-  }
-
-  render () {
-    const { width, draggingOver } = this.state;
-    const { children } = this.props;
-
-    let mountedColumns;
-
-    if (isMobile(width)) {
-      mountedColumns = (
-        <ColumnsArea>
-          {children}
-        </ColumnsArea>
-      );
-    } else {
-      mountedColumns = (
-        <ColumnsArea>
-          <Compose withHeader={true} />
-          <HomeTimeline shouldUpdateScroll={() => false} />
-          <Notifications shouldUpdateScroll={() => false} />
-          <div style={{display: 'flex', flex: '1 1 auto', position: 'relative'}}>{children}</div>
-        </ColumnsArea>
-      );
-    }
-
-    return (
-      <div className='ui' ref={this.setRef}>
-        <TabsBar />
-
-        {mountedColumns}
-
-        <NotificationsContainer />
-        <LoadingBarContainer className="loading-bar" />
-        <ModalContainer />
-        <UploadArea active={draggingOver} onClose={this.closeUploadModal} />
-      </div>
-    );
-  }
-
-}
-
-UI.propTypes = {
-  dispatch: PropTypes.func.isRequired,
-  children: PropTypes.node
-};
-
-export default connect()(UI);