Redesign public hashtag page to use a masonry layout (#9822)
This commit is contained in:
		
							parent
							
								
									4ab42287c0
								
							
						
					
					
						commit
						bc642ac24b
					
				
					 11 changed files with 392 additions and 77 deletions
				
			
		| 
						 | 
					@ -3,6 +3,8 @@
 | 
				
			||||||
class TagsController < ApplicationController
 | 
					class TagsController < ApplicationController
 | 
				
			||||||
  PAGE_SIZE = 20
 | 
					  PAGE_SIZE = 20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  layout 'public'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  before_action :set_body_classes
 | 
					  before_action :set_body_classes
 | 
				
			||||||
  before_action :set_instance_presenter
 | 
					  before_action :set_instance_presenter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,17 @@
 | 
				
			||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
					import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class DisplayName extends React.PureComponent {
 | 
					export default class DisplayName extends React.PureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static propTypes = {
 | 
					  static propTypes = {
 | 
				
			||||||
    account: ImmutablePropTypes.map.isRequired,
 | 
					    account: ImmutablePropTypes.map.isRequired,
 | 
				
			||||||
    others: ImmutablePropTypes.list,
 | 
					    others: ImmutablePropTypes.list,
 | 
				
			||||||
 | 
					    localDomain: PropTypes.string,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
    const { account, others } = this.props;
 | 
					    const { account, others, localDomain } = this.props;
 | 
				
			||||||
    const displayNameHtml = { __html: account.get('display_name_html') };
 | 
					    const displayNameHtml = { __html: account.get('display_name_html') };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let suffix;
 | 
					    let suffix;
 | 
				
			||||||
| 
						 | 
					@ -17,7 +19,13 @@ export default class DisplayName extends React.PureComponent {
 | 
				
			||||||
    if (others && others.size > 1) {
 | 
					    if (others && others.size > 1) {
 | 
				
			||||||
      suffix = `+${others.size}`;
 | 
					      suffix = `+${others.size}`;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      suffix = <span className='display-name__account'>@{account.get('acct')}</span>;
 | 
					      let acct = account.get('acct');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (acct.indexOf('@') === -1 && localDomain) {
 | 
				
			||||||
 | 
					        acct = `${acct}@${localDomain}`;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      suffix = <span className='display-name__account'>@{acct}</span>;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -77,7 +77,7 @@ class Status extends ImmutablePureComponent {
 | 
				
			||||||
    'account',
 | 
					    'account',
 | 
				
			||||||
    'muted',
 | 
					    'muted',
 | 
				
			||||||
    'hidden',
 | 
					    'hidden',
 | 
				
			||||||
  ]
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleClick = () => {
 | 
					  handleClick = () => {
 | 
				
			||||||
    if (this.props.onClick) {
 | 
					    if (this.props.onClick) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,28 +1,32 @@
 | 
				
			||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import { connect } from 'react-redux';
 | 
					import { connect } from 'react-redux';
 | 
				
			||||||
import PropTypes from 'prop-types';
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
import StatusListContainer from '../../ui/containers/status_list_container';
 | 
					import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
				
			||||||
import { expandHashtagTimeline } from '../../../actions/timelines';
 | 
					import { expandHashtagTimeline } from '../../../actions/timelines';
 | 
				
			||||||
import Column from '../../../components/column';
 | 
					 | 
				
			||||||
import ColumnHeader from '../../../components/column_header';
 | 
					 | 
				
			||||||
import { connectHashtagStream } from '../../../actions/streaming';
 | 
					import { connectHashtagStream } from '../../../actions/streaming';
 | 
				
			||||||
 | 
					import Masonry from 'react-masonry-infinite';
 | 
				
			||||||
 | 
					import { List as ImmutableList } from 'immutable';
 | 
				
			||||||
 | 
					import DetailedStatusContainer from '../../status/containers/detailed_status_container';
 | 
				
			||||||
 | 
					import { debounce } from 'lodash';
 | 
				
			||||||
 | 
					import LoadingIndicator from '../../../components/loading_indicator';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default @connect()
 | 
					const mapStateToProps = (state, { hashtag }) => ({
 | 
				
			||||||
 | 
					  statusIds: state.getIn(['timelines', `hashtag:${hashtag}`, 'items'], ImmutableList()),
 | 
				
			||||||
 | 
					  isLoading: state.getIn(['timelines', `hashtag:${hashtag}`, 'isLoading'], false),
 | 
				
			||||||
 | 
					  hasMore: state.getIn(['timelines', `hashtag:${hashtag}`, 'hasMore'], false),
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default @connect(mapStateToProps)
 | 
				
			||||||
class HashtagTimeline extends React.PureComponent {
 | 
					class HashtagTimeline extends React.PureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static propTypes = {
 | 
					  static propTypes = {
 | 
				
			||||||
    dispatch: PropTypes.func.isRequired,
 | 
					    dispatch: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					    statusIds: ImmutablePropTypes.list.isRequired,
 | 
				
			||||||
 | 
					    isLoading: PropTypes.bool.isRequired,
 | 
				
			||||||
 | 
					    hasMore: PropTypes.bool.isRequired,
 | 
				
			||||||
    hashtag: PropTypes.string.isRequired,
 | 
					    hashtag: PropTypes.string.isRequired,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleHeaderClick = () => {
 | 
					 | 
				
			||||||
    this.column.scrollTop();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  setRef = c => {
 | 
					 | 
				
			||||||
    this.column = c;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  componentDidMount () {
 | 
					  componentDidMount () {
 | 
				
			||||||
    const { dispatch, hashtag } = this.props;
 | 
					    const { dispatch, hashtag } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,28 +41,52 @@ class HashtagTimeline extends React.PureComponent {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleLoadMore = maxId => {
 | 
					  handleLoadMore = () => {
 | 
				
			||||||
    this.props.dispatch(expandHashtagTimeline(this.props.hashtag, { maxId }));
 | 
					    const maxId = this.props.statusIds.last();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (maxId) {
 | 
				
			||||||
 | 
					      this.props.dispatch(expandHashtagTimeline(this.props.hashtag, { maxId }));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setRef = c => {
 | 
				
			||||||
 | 
					    this.masonry = c;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleHeightChange = debounce(() => {
 | 
				
			||||||
 | 
					    if (!this.masonry) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.masonry.forcePack();
 | 
				
			||||||
 | 
					  }, 50)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
    const { hashtag } = this.props;
 | 
					    const { statusIds, hasMore, isLoading } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const sizes = [
 | 
				
			||||||
 | 
					      { columns: 1, gutter: 0 },
 | 
				
			||||||
 | 
					      { mq: '415px', columns: 1, gutter: 10 },
 | 
				
			||||||
 | 
					      { mq: '640px', columns: 2, gutter: 10 },
 | 
				
			||||||
 | 
					      { mq: '960px', columns: 3, gutter: 10 },
 | 
				
			||||||
 | 
					      { mq: '1255px', columns: 3, gutter: 10 },
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const loader = (isLoading && statusIds.isEmpty()) ? <LoadingIndicator key={0} /> : undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <Column ref={this.setRef}>
 | 
					      <Masonry ref={this.setRef} className='statuses-grid' hasMore={hasMore} loadMore={this.handleLoadMore} sizes={sizes} loader={loader}>
 | 
				
			||||||
        <ColumnHeader
 | 
					        {statusIds.map(statusId => (
 | 
				
			||||||
          icon='hashtag'
 | 
					          <div className='statuses-grid__item' key={statusId}>
 | 
				
			||||||
          title={hashtag}
 | 
					            <DetailedStatusContainer
 | 
				
			||||||
          onClick={this.handleHeaderClick}
 | 
					              id={statusId}
 | 
				
			||||||
        />
 | 
					              showThread
 | 
				
			||||||
 | 
					              measureHeight
 | 
				
			||||||
        <StatusListContainer
 | 
					              onHeightChange={this.handleHeightChange}
 | 
				
			||||||
          trackScroll={false}
 | 
					            />
 | 
				
			||||||
          scrollKey='standalone_hashtag_timeline'
 | 
					          </div>
 | 
				
			||||||
          timelineId={`hashtag:${hashtag}`}
 | 
					        )).toArray()}
 | 
				
			||||||
          onLoadMore={this.handleLoadMore}
 | 
					      </Masonry>
 | 
				
			||||||
        />
 | 
					 | 
				
			||||||
      </Column>
 | 
					 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,7 @@ import { FormattedDate, FormattedNumber } from 'react-intl';
 | 
				
			||||||
import Card from './card';
 | 
					import Card from './card';
 | 
				
			||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
 | 
					import ImmutablePureComponent from 'react-immutable-pure-component';
 | 
				
			||||||
import Video from '../../video';
 | 
					import Video from '../../video';
 | 
				
			||||||
 | 
					import scheduleIdleTask from '../../ui/util/schedule_idle_task';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class DetailedStatus extends ImmutablePureComponent {
 | 
					export default class DetailedStatus extends ImmutablePureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,10 +24,17 @@ export default class DetailedStatus extends ImmutablePureComponent {
 | 
				
			||||||
    onOpenMedia: PropTypes.func.isRequired,
 | 
					    onOpenMedia: PropTypes.func.isRequired,
 | 
				
			||||||
    onOpenVideo: PropTypes.func.isRequired,
 | 
					    onOpenVideo: PropTypes.func.isRequired,
 | 
				
			||||||
    onToggleHidden: PropTypes.func.isRequired,
 | 
					    onToggleHidden: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					    measureHeight: PropTypes.bool,
 | 
				
			||||||
 | 
					    onHeightChange: PropTypes.func,
 | 
				
			||||||
 | 
					    domain: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  state = {
 | 
				
			||||||
 | 
					    height: null,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleAccountClick = (e) => {
 | 
					  handleAccountClick = (e) => {
 | 
				
			||||||
    if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
 | 
					    if (e.button === 0 && !(e.ctrlKey || e.metaKey) && this.context.router) {
 | 
				
			||||||
      e.preventDefault();
 | 
					      e.preventDefault();
 | 
				
			||||||
      this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
 | 
					      this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -42,13 +50,56 @@ export default class DetailedStatus extends ImmutablePureComponent {
 | 
				
			||||||
    this.props.onToggleHidden(this.props.status);
 | 
					    this.props.onToggleHidden(this.props.status);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  _measureHeight (heightJustChanged) {
 | 
				
			||||||
 | 
					    if (this.props.measureHeight && this.node) {
 | 
				
			||||||
 | 
					      scheduleIdleTask(() => this.node && this.setState({ height: this.node.offsetHeight }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (this.props.onHeightChange && heightJustChanged) {
 | 
				
			||||||
 | 
					        this.props.onHeightChange();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setRef = c => {
 | 
				
			||||||
 | 
					    this.node = c;
 | 
				
			||||||
 | 
					    this._measureHeight();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  componentDidUpdate (prevProps, prevState) {
 | 
				
			||||||
 | 
					    this._measureHeight(prevState.height !== this.state.height);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleModalLink = e => {
 | 
				
			||||||
 | 
					    e.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let href;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (e.target.nodeName !== 'A') {
 | 
				
			||||||
 | 
					      href = e.target.parentNode.href;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      href = e.target.href;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    window.open(href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
    const status = this.props.status.get('reblog') ? this.props.status.get('reblog') : this.props.status;
 | 
					    const status = this.props.status.get('reblog') ? this.props.status.get('reblog') : this.props.status;
 | 
				
			||||||
 | 
					    const outerStyle = { boxSizing: 'border-box' };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!status) {
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let media           = '';
 | 
					    let media           = '';
 | 
				
			||||||
    let applicationLink = '';
 | 
					    let applicationLink = '';
 | 
				
			||||||
    let reblogLink = '';
 | 
					    let reblogLink = '';
 | 
				
			||||||
    let reblogIcon = 'retweet';
 | 
					    let reblogIcon = 'retweet';
 | 
				
			||||||
 | 
					    let favouriteLink = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (this.props.measureHeight) {
 | 
				
			||||||
 | 
					      outerStyle.height = `${this.state.height}px`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (status.get('media_attachments').size > 0) {
 | 
					    if (status.get('media_attachments').size > 0) {
 | 
				
			||||||
      if (status.get('media_attachments').some(item => item.get('type') === 'unknown')) {
 | 
					      if (status.get('media_attachments').some(item => item.get('type') === 'unknown')) {
 | 
				
			||||||
| 
						 | 
					@ -95,20 +146,51 @@ export default class DetailedStatus extends ImmutablePureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (status.get('visibility') === 'private') {
 | 
					    if (status.get('visibility') === 'private') {
 | 
				
			||||||
      reblogLink = <i className={`fa fa-${reblogIcon}`} />;
 | 
					      reblogLink = <i className={`fa fa-${reblogIcon}`} />;
 | 
				
			||||||
 | 
					    } else if (this.context.router) {
 | 
				
			||||||
 | 
					      reblogLink = (
 | 
				
			||||||
 | 
					        <Link to={`/statuses/${status.get('id')}/reblogs`} className='detailed-status__link'>
 | 
				
			||||||
 | 
					          <i className={`fa fa-${reblogIcon}`} />
 | 
				
			||||||
 | 
					          <span className='detailed-status__reblogs'>
 | 
				
			||||||
 | 
					            <FormattedNumber value={status.get('reblogs_count')} />
 | 
				
			||||||
 | 
					          </span>
 | 
				
			||||||
 | 
					        </Link>
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      reblogLink = (<Link to={`/statuses/${status.get('id')}/reblogs`} className='detailed-status__link'>
 | 
					      reblogLink = (
 | 
				
			||||||
        <i className={`fa fa-${reblogIcon}`} />
 | 
					        <a href={`/interact/${status.get('id')}?type=reblog`} className='detailed-status__link' onClick={this.handleModalLink}>
 | 
				
			||||||
        <span className='detailed-status__reblogs'>
 | 
					          <i className={`fa fa-${reblogIcon}`} />
 | 
				
			||||||
          <FormattedNumber value={status.get('reblogs_count')} />
 | 
					          <span className='detailed-status__reblogs'>
 | 
				
			||||||
        </span>
 | 
					            <FormattedNumber value={status.get('reblogs_count')} />
 | 
				
			||||||
      </Link>);
 | 
					          </span>
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (this.context.router) {
 | 
				
			||||||
 | 
					      favouriteLink = (
 | 
				
			||||||
 | 
					        <Link to={`/statuses/${status.get('id')}/favourites`} className='detailed-status__link'>
 | 
				
			||||||
 | 
					          <i className='fa fa-star' />
 | 
				
			||||||
 | 
					          <span className='detailed-status__favorites'>
 | 
				
			||||||
 | 
					            <FormattedNumber value={status.get('favourites_count')} />
 | 
				
			||||||
 | 
					          </span>
 | 
				
			||||||
 | 
					        </Link>
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      favouriteLink = (
 | 
				
			||||||
 | 
					        <a href={`/interact/${status.get('id')}?type=favourite`} className='detailed-status__link' onClick={this.handleModalLink}>
 | 
				
			||||||
 | 
					          <i className='fa fa-star' />
 | 
				
			||||||
 | 
					          <span className='detailed-status__favorites'>
 | 
				
			||||||
 | 
					            <FormattedNumber value={status.get('favourites_count')} />
 | 
				
			||||||
 | 
					          </span>
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <div className='detailed-status'>
 | 
					      <div ref={this.setRef} className='detailed-status' style={outerStyle}>
 | 
				
			||||||
        <a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='detailed-status__display-name'>
 | 
					        <a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='detailed-status__display-name'>
 | 
				
			||||||
          <div className='detailed-status__display-avatar'><Avatar account={status.get('account')} size={48} /></div>
 | 
					          <div className='detailed-status__display-avatar'><Avatar account={status.get('account')} size={48} /></div>
 | 
				
			||||||
          <DisplayName account={status.get('account')} />
 | 
					          <DisplayName account={status.get('account')} localDomain={this.props.domain} />
 | 
				
			||||||
        </a>
 | 
					        </a>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <StatusContent status={status} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} />
 | 
					        <StatusContent status={status} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} />
 | 
				
			||||||
| 
						 | 
					@ -118,12 +200,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
 | 
				
			||||||
        <div className='detailed-status__meta'>
 | 
					        <div className='detailed-status__meta'>
 | 
				
			||||||
          <a className='detailed-status__datetime' href={status.get('url')} target='_blank' rel='noopener'>
 | 
					          <a className='detailed-status__datetime' href={status.get('url')} target='_blank' rel='noopener'>
 | 
				
			||||||
            <FormattedDate value={new Date(status.get('created_at'))} hour12={false} year='numeric' month='short' day='2-digit' hour='2-digit' minute='2-digit' />
 | 
					            <FormattedDate value={new Date(status.get('created_at'))} hour12={false} year='numeric' month='short' day='2-digit' hour='2-digit' minute='2-digit' />
 | 
				
			||||||
          </a>{applicationLink} · {reblogLink} · <Link to={`/statuses/${status.get('id')}/favourites`} className='detailed-status__link'>
 | 
					          </a>{applicationLink} · {reblogLink} · {favouriteLink}
 | 
				
			||||||
            <i className='fa fa-star' />
 | 
					 | 
				
			||||||
            <span className='detailed-status__favorites'>
 | 
					 | 
				
			||||||
              <FormattedNumber value={status.get('favourites_count')} />
 | 
					 | 
				
			||||||
            </span>
 | 
					 | 
				
			||||||
          </Link>
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,172 @@
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { connect } from 'react-redux';
 | 
				
			||||||
 | 
					import DetailedStatus from '../components/detailed_status';
 | 
				
			||||||
 | 
					import { makeGetStatus } from '../../../selectors';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  replyCompose,
 | 
				
			||||||
 | 
					  mentionCompose,
 | 
				
			||||||
 | 
					  directCompose,
 | 
				
			||||||
 | 
					} from '../../../actions/compose';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  reblog,
 | 
				
			||||||
 | 
					  favourite,
 | 
				
			||||||
 | 
					  unreblog,
 | 
				
			||||||
 | 
					  unfavourite,
 | 
				
			||||||
 | 
					  pin,
 | 
				
			||||||
 | 
					  unpin,
 | 
				
			||||||
 | 
					} from '../../../actions/interactions';
 | 
				
			||||||
 | 
					import { blockAccount } from '../../../actions/accounts';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  muteStatus,
 | 
				
			||||||
 | 
					  unmuteStatus,
 | 
				
			||||||
 | 
					  deleteStatus,
 | 
				
			||||||
 | 
					  hideStatus,
 | 
				
			||||||
 | 
					  revealStatus,
 | 
				
			||||||
 | 
					} from '../../../actions/statuses';
 | 
				
			||||||
 | 
					import { initMuteModal } from '../../../actions/mutes';
 | 
				
			||||||
 | 
					import { initReport } from '../../../actions/reports';
 | 
				
			||||||
 | 
					import { openModal } from '../../../actions/modal';
 | 
				
			||||||
 | 
					import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 | 
				
			||||||
 | 
					import { boostModal, deleteModal } from '../../../initial_state';
 | 
				
			||||||
 | 
					import { showAlertForError } from '../../../actions/alerts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const messages = defineMessages({
 | 
				
			||||||
 | 
					  deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
 | 
				
			||||||
 | 
					  deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
 | 
				
			||||||
 | 
					  redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
 | 
				
			||||||
 | 
					  redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' },
 | 
				
			||||||
 | 
					  blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' },
 | 
				
			||||||
 | 
					  replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
 | 
				
			||||||
 | 
					  replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const makeMapStateToProps = () => {
 | 
				
			||||||
 | 
					  const getStatus = makeGetStatus();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const mapStateToProps = (state, props) => ({
 | 
				
			||||||
 | 
					    status: getStatus(state, props),
 | 
				
			||||||
 | 
					    domain: state.getIn(['meta', 'domain']),
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return mapStateToProps;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const mapDispatchToProps = (dispatch, { intl }) => ({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onReply (status, router) {
 | 
				
			||||||
 | 
					    dispatch((_, getState) => {
 | 
				
			||||||
 | 
					      let state = getState();
 | 
				
			||||||
 | 
					      if (state.getIn(['compose', 'text']).trim().length !== 0) {
 | 
				
			||||||
 | 
					        dispatch(openModal('CONFIRM', {
 | 
				
			||||||
 | 
					          message: intl.formatMessage(messages.replyMessage),
 | 
				
			||||||
 | 
					          confirm: intl.formatMessage(messages.replyConfirm),
 | 
				
			||||||
 | 
					          onConfirm: () => dispatch(replyCompose(status, router)),
 | 
				
			||||||
 | 
					        }));
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        dispatch(replyCompose(status, router));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onModalReblog (status) {
 | 
				
			||||||
 | 
					    dispatch(reblog(status));
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onReblog (status, e) {
 | 
				
			||||||
 | 
					    if (status.get('reblogged')) {
 | 
				
			||||||
 | 
					      dispatch(unreblog(status));
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      if (e.shiftKey || !boostModal) {
 | 
				
			||||||
 | 
					        this.onModalReblog(status);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog }));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onFavourite (status) {
 | 
				
			||||||
 | 
					    if (status.get('favourited')) {
 | 
				
			||||||
 | 
					      dispatch(unfavourite(status));
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      dispatch(favourite(status));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onPin (status) {
 | 
				
			||||||
 | 
					    if (status.get('pinned')) {
 | 
				
			||||||
 | 
					      dispatch(unpin(status));
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      dispatch(pin(status));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onEmbed (status) {
 | 
				
			||||||
 | 
					    dispatch(openModal('EMBED', {
 | 
				
			||||||
 | 
					      url: status.get('url'),
 | 
				
			||||||
 | 
					      onError: error => dispatch(showAlertForError(error)),
 | 
				
			||||||
 | 
					    }));
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onDelete (status, history, withRedraft = false) {
 | 
				
			||||||
 | 
					    if (!deleteModal) {
 | 
				
			||||||
 | 
					      dispatch(deleteStatus(status.get('id'), history, withRedraft));
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      dispatch(openModal('CONFIRM', {
 | 
				
			||||||
 | 
					        message: intl.formatMessage(withRedraft ? messages.redraftMessage : messages.deleteMessage),
 | 
				
			||||||
 | 
					        confirm: intl.formatMessage(withRedraft ? messages.redraftConfirm : messages.deleteConfirm),
 | 
				
			||||||
 | 
					        onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)),
 | 
				
			||||||
 | 
					      }));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onDirect (account, router) {
 | 
				
			||||||
 | 
					    dispatch(directCompose(account, router));
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onMention (account, router) {
 | 
				
			||||||
 | 
					    dispatch(mentionCompose(account, router));
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onOpenMedia (media, index) {
 | 
				
			||||||
 | 
					    dispatch(openModal('MEDIA', { media, index }));
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onOpenVideo (media, time) {
 | 
				
			||||||
 | 
					    dispatch(openModal('VIDEO', { media, time }));
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onBlock (account) {
 | 
				
			||||||
 | 
					    dispatch(openModal('CONFIRM', {
 | 
				
			||||||
 | 
					      message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
 | 
				
			||||||
 | 
					      confirm: intl.formatMessage(messages.blockConfirm),
 | 
				
			||||||
 | 
					      onConfirm: () => dispatch(blockAccount(account.get('id'))),
 | 
				
			||||||
 | 
					    }));
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onReport (status) {
 | 
				
			||||||
 | 
					    dispatch(initReport(status.get('account'), status));
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onMute (account) {
 | 
				
			||||||
 | 
					    dispatch(initMuteModal(account));
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onMuteConversation (status) {
 | 
				
			||||||
 | 
					    if (status.get('muted')) {
 | 
				
			||||||
 | 
					      dispatch(unmuteStatus(status.get('id')));
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      dispatch(muteStatus(status.get('id')));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onToggleHidden (status) {
 | 
				
			||||||
 | 
					    if (status.get('hidden')) {
 | 
				
			||||||
 | 
					      dispatch(revealStatus(status.get('id')));
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      dispatch(hideStatus(status.get('id')));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(DetailedStatus));
 | 
				
			||||||
| 
						 | 
					@ -425,3 +425,30 @@
 | 
				
			||||||
    border-radius: 0;
 | 
					    border-radius: 0;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$maximum-width: 1235px;
 | 
				
			||||||
 | 
					$fluid-breakpoint: $maximum-width + 20px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.statuses-grid {
 | 
				
			||||||
 | 
					  min-height: 600px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &__item {
 | 
				
			||||||
 | 
					    width: (960px - 20px) / 3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @media screen and (max-width: $fluid-breakpoint) {
 | 
				
			||||||
 | 
					      width: (940px - 20px) / 3;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @media screen and (max-width: $no-gap-breakpoint) {
 | 
				
			||||||
 | 
					      width: 100vw;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .detailed-status {
 | 
				
			||||||
 | 
					    border-radius: 4px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @media screen and (max-width: $no-gap-breakpoint) {
 | 
				
			||||||
 | 
					      border-bottom: 1px solid lighten($ui-base-color, 12%);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,33 +8,5 @@
 | 
				
			||||||
  = javascript_pack_tag 'about', integrity: true, crossorigin: 'anonymous'
 | 
					  = javascript_pack_tag 'about', integrity: true, crossorigin: 'anonymous'
 | 
				
			||||||
  = render 'og'
 | 
					  = render 'og'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.landing-page.tag-page.alternative
 | 
					#mastodon-timeline{ data: { props: Oj.dump(default_props.merge(hashtag: @tag.name)) } }
 | 
				
			||||||
  .features
 | 
					 | 
				
			||||||
    .container
 | 
					 | 
				
			||||||
      .grid
 | 
					 | 
				
			||||||
        .column-1
 | 
					 | 
				
			||||||
          #mastodon-timeline{ data: { props: Oj.dump(default_props.merge(hashtag: @tag.name)) } }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .column-2
 | 
					 | 
				
			||||||
          .about-mastodon
 | 
					 | 
				
			||||||
            .about-hashtag.landing-page__information
 | 
					 | 
				
			||||||
              .brand
 | 
					 | 
				
			||||||
                = link_to root_url do
 | 
					 | 
				
			||||||
                  = image_tag asset_pack_path('logo_full.svg'), alt: 'Mastodon'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
              %p= t 'about.about_hashtag_html', hashtag: @tag.name
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
              .cta
 | 
					 | 
				
			||||||
                - if user_signed_in?
 | 
					 | 
				
			||||||
                  = link_to t('settings.back'), root_path, class: 'button button-secondary'
 | 
					 | 
				
			||||||
                - else
 | 
					 | 
				
			||||||
                  = link_to t('auth.login'), new_user_session_path, class: 'button button-secondary'
 | 
					 | 
				
			||||||
                = link_to t('about.learn_more'), about_path, class: 'button button-alternative'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            .landing-page__features.landing-page__information
 | 
					 | 
				
			||||||
              %h3= t 'about.what_is_mastodon'
 | 
					 | 
				
			||||||
              %p= t 'about.about_mastodon_html'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
              = render 'features'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#modal-container
 | 
					#modal-container
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -98,6 +98,7 @@
 | 
				
			||||||
    "react-immutable-proptypes": "^2.1.0",
 | 
					    "react-immutable-proptypes": "^2.1.0",
 | 
				
			||||||
    "react-immutable-pure-component": "^1.1.1",
 | 
					    "react-immutable-pure-component": "^1.1.1",
 | 
				
			||||||
    "react-intl": "^2.7.2",
 | 
					    "react-intl": "^2.7.2",
 | 
				
			||||||
 | 
					    "react-masonry-infinite": "^1.2.2",
 | 
				
			||||||
    "react-motion": "^0.5.2",
 | 
					    "react-motion": "^0.5.2",
 | 
				
			||||||
    "react-notification": "^6.8.4",
 | 
					    "react-notification": "^6.8.4",
 | 
				
			||||||
    "react-overlays": "^0.8.3",
 | 
					    "react-overlays": "^0.8.3",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@ RSpec.describe TagsController, type: :controller do
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      it 'renders application layout' do
 | 
					      it 'renders application layout' do
 | 
				
			||||||
        get :show, params: { id: 'test', max_id: late.id }
 | 
					        get :show, params: { id: 'test', max_id: late.id }
 | 
				
			||||||
        expect(response).to render_template layout: 'application'
 | 
					        expect(response).to render_template layout: 'public'
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										28
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								yarn.lock
									
									
									
									
									
								
							| 
						 | 
					@ -1681,6 +1681,13 @@ braces@^2.3.0, braces@^2.3.1:
 | 
				
			||||||
    split-string "^3.0.2"
 | 
					    split-string "^3.0.2"
 | 
				
			||||||
    to-regex "^3.0.1"
 | 
					    to-regex "^3.0.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bricks.js@^1.7.0:
 | 
				
			||||||
 | 
					  version "1.8.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/bricks.js/-/bricks.js-1.8.0.tgz#8fdeb3c0226af251f4d5727a7df7f9ac0092b4b2"
 | 
				
			||||||
 | 
					  integrity sha1-j96zwCJq8lH01XJ6fff5rACStLI=
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    knot.js "^1.1.5"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
brorand@^1.0.1:
 | 
					brorand@^1.0.1:
 | 
				
			||||||
  version "1.1.0"
 | 
					  version "1.1.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
 | 
					  resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
 | 
				
			||||||
| 
						 | 
					@ -5528,6 +5535,11 @@ kleur@^2.0.1:
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/kleur/-/kleur-2.0.2.tgz#b704f4944d95e255d038f0cb05fb8a602c55a300"
 | 
					  resolved "https://registry.yarnpkg.com/kleur/-/kleur-2.0.2.tgz#b704f4944d95e255d038f0cb05fb8a602c55a300"
 | 
				
			||||||
  integrity sha512-77XF9iTllATmG9lSlIv0qdQ2BQ/h9t0bJllHlbvsQ0zUWfU7Yi0S8L5JXzPZgkefIiajLmBJJ4BsMJmqcf7oxQ==
 | 
					  integrity sha512-77XF9iTllATmG9lSlIv0qdQ2BQ/h9t0bJllHlbvsQ0zUWfU7Yi0S8L5JXzPZgkefIiajLmBJJ4BsMJmqcf7oxQ==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					knot.js@^1.1.5:
 | 
				
			||||||
 | 
					  version "1.1.5"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/knot.js/-/knot.js-1.1.5.tgz#28e72522f703f50fe98812fde224dd72728fef5d"
 | 
				
			||||||
 | 
					  integrity sha1-KOclIvcD9Q/piBL94iTdcnKP710=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
lcid@^1.0.0:
 | 
					lcid@^1.0.0:
 | 
				
			||||||
  version "1.0.0"
 | 
					  version "1.0.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
 | 
					  resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
 | 
				
			||||||
| 
						 | 
					@ -7558,6 +7570,13 @@ react-immutable-pure-component@^1.1.1:
 | 
				
			||||||
  optionalDependencies:
 | 
					  optionalDependencies:
 | 
				
			||||||
    "@types/react" "16.4.6"
 | 
					    "@types/react" "16.4.6"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					react-infinite-scroller@^1.0.12:
 | 
				
			||||||
 | 
					  version "1.2.4"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/react-infinite-scroller/-/react-infinite-scroller-1.2.4.tgz#f67eaec4940a4ce6417bebdd6e3433bfc38826e9"
 | 
				
			||||||
 | 
					  integrity sha512-/oOa0QhZjXPqaD6sictN2edFMsd3kkMiE19Vcz5JDgHpzEJVqYcmq+V3mkwO88087kvKGe1URNksHEOt839Ubw==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    prop-types "^15.5.8"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
react-input-autosize@^2.2.1:
 | 
					react-input-autosize@^2.2.1:
 | 
				
			||||||
  version "2.2.1"
 | 
					  version "2.2.1"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-2.2.1.tgz#ec428fa15b1592994fb5f9aa15bb1eb6baf420f8"
 | 
					  resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-2.2.1.tgz#ec428fa15b1592994fb5f9aa15bb1eb6baf420f8"
 | 
				
			||||||
| 
						 | 
					@ -7596,6 +7615,15 @@ react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4:
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
 | 
					  resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
 | 
				
			||||||
  integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
 | 
					  integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					react-masonry-infinite@^1.2.2:
 | 
				
			||||||
 | 
					  version "1.2.2"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/react-masonry-infinite/-/react-masonry-infinite-1.2.2.tgz#20c1386f9ccdda9747527c8f42bc2c02dd2e7951"
 | 
				
			||||||
 | 
					  integrity sha1-IME4b5zN2pdHUnyPQrwsAt0ueVE=
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    bricks.js "^1.7.0"
 | 
				
			||||||
 | 
					    prop-types "^15.5.10"
 | 
				
			||||||
 | 
					    react-infinite-scroller "^1.0.12"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
react-motion@^0.5.2:
 | 
					react-motion@^0.5.2:
 | 
				
			||||||
  version "0.5.2"
 | 
					  version "0.5.2"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/react-motion/-/react-motion-0.5.2.tgz#0dd3a69e411316567927917c6626551ba0607316"
 | 
					  resolved "https://registry.yarnpkg.com/react-motion/-/react-motion-0.5.2.tgz#0dd3a69e411316567927917c6626551ba0607316"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue