diff options
author | Eugen Rochko <eugen@zeonfederated.com> | 2019-01-16 19:47:46 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-16 19:47:46 +0100 |
commit | bc642ac24b49c14dca382e7aabbc16130293d2f4 (patch) | |
tree | 1bf55901185d1cac954ef216f7394d09c10df1f3 /app/javascript/mastodon/features/standalone | |
parent | 4ab42287c0bbcbd259bff229d66da8964a261aff (diff) |
Redesign public hashtag page to use a masonry layout (#9822)
Diffstat (limited to 'app/javascript/mastodon/features/standalone')
-rw-r--r-- | app/javascript/mastodon/features/standalone/hashtag_timeline/index.js | 86 |
1 files changed, 57 insertions, 29 deletions
diff --git a/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js b/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js index 759922638..4e09b1948 100644 --- a/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js +++ b/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js @@ -1,28 +1,32 @@ import React from 'react'; import { connect } from 'react-redux'; 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 Column from '../../../components/column'; -import ColumnHeader from '../../../components/column_header'; 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 { static propTypes = { dispatch: PropTypes.func.isRequired, + statusIds: ImmutablePropTypes.list.isRequired, + isLoading: PropTypes.bool.isRequired, + hasMore: PropTypes.bool.isRequired, hashtag: PropTypes.string.isRequired, }; - handleHeaderClick = () => { - this.column.scrollTop(); - } - - setRef = c => { - this.column = c; - } - componentDidMount () { const { dispatch, hashtag } = this.props; @@ -37,28 +41,52 @@ class HashtagTimeline extends React.PureComponent { } } - handleLoadMore = maxId => { - this.props.dispatch(expandHashtagTimeline(this.props.hashtag, { maxId })); + handleLoadMore = () => { + 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 () { - 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 ( - <Column ref={this.setRef}> - <ColumnHeader - icon='hashtag' - title={hashtag} - onClick={this.handleHeaderClick} - /> - - <StatusListContainer - trackScroll={false} - scrollKey='standalone_hashtag_timeline' - timelineId={`hashtag:${hashtag}`} - onLoadMore={this.handleLoadMore} - /> - </Column> + <Masonry ref={this.setRef} className='statuses-grid' hasMore={hasMore} loadMore={this.handleLoadMore} sizes={sizes} loader={loader}> + {statusIds.map(statusId => ( + <div className='statuses-grid__item' key={statusId}> + <DetailedStatusContainer + id={statusId} + showThread + measureHeight + onHeightChange={this.handleHeightChange} + /> + </div> + )).toArray()} + </Masonry> ); } |