From 3e8b6239751673a0672b1a51c6c7f0a7d5e1eab8 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Sat, 19 Jan 2019 18:55:27 +0100 Subject: [Glitch] Redesign public hashtag page to use a masonry layout Port bc642ac24b49c14dca382e7aabbc16130293d2f4 to glitch flavour --- .../features/standalone/hashtag_timeline/index.js | 88 ++++++++++++++-------- 1 file changed, 58 insertions(+), 30 deletions(-) (limited to 'app/javascript/flavours/glitch/features/standalone/hashtag_timeline') diff --git a/app/javascript/flavours/glitch/features/standalone/hashtag_timeline/index.js b/app/javascript/flavours/glitch/features/standalone/hashtag_timeline/index.js index 44ba8db92..e45088771 100644 --- a/app/javascript/flavours/glitch/features/standalone/hashtag_timeline/index.js +++ b/app/javascript/flavours/glitch/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 'flavours/glitch/features/ui/containers/status_list_container'; +import ImmutablePropTypes from 'react-immutable-proptypes'; import { expandHashtagTimeline } from 'flavours/glitch/actions/timelines'; -import Column from 'flavours/glitch/components/column'; -import ColumnHeader from 'flavours/glitch/components/column_header'; import { connectHashtagStream } from 'flavours/glitch/actions/streaming'; +import Masonry from 'react-masonry-infinite'; +import { List as ImmutableList } from 'immutable'; +import DetailedStatusContainer from 'flavours/glitch/features/status/containers/detailed_status_container'; +import { debounce } from 'lodash'; +import LoadingIndicator from 'flavours/glitch/components/loading_indicator'; -@connect() -export default class HashtagTimeline extends React.PureComponent { +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 @@ export default 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()) ? : undefined; return ( - - - - - + + {statusIds.map(statusId => ( +
+ +
+ )).toArray()} +
); } -- cgit From 02295257b305371c574d9b4aad1377e7b8acdd69 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Sun, 20 Jan 2019 11:47:17 +0100 Subject: [Glitch] Improve the public hashtag page Port 8b1990355974543542544e56d2046bc0c9c8716b to glitch-soc --- .../features/standalone/hashtag_timeline/index.js | 2 +- .../features/status/components/detailed_status.js | 45 ++++++++++++---------- .../flavours/glitch/features/status/index.js | 5 ++- app/javascript/flavours/glitch/styles/widgets.scss | 28 ++++++++++++++ 4 files changed, 58 insertions(+), 22 deletions(-) (limited to 'app/javascript/flavours/glitch/features/standalone/hashtag_timeline') diff --git a/app/javascript/flavours/glitch/features/standalone/hashtag_timeline/index.js b/app/javascript/flavours/glitch/features/standalone/hashtag_timeline/index.js index e45088771..17f064713 100644 --- a/app/javascript/flavours/glitch/features/standalone/hashtag_timeline/index.js +++ b/app/javascript/flavours/glitch/features/standalone/hashtag_timeline/index.js @@ -80,7 +80,7 @@ class HashtagTimeline extends React.PureComponent {
diff --git a/app/javascript/flavours/glitch/features/status/components/detailed_status.js b/app/javascript/flavours/glitch/features/status/components/detailed_status.js index ba44ad3de..447247567 100644 --- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js +++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js @@ -13,6 +13,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import Video from 'flavours/glitch/features/video'; import VisibilityIcon from 'flavours/glitch/components/status_visibility_icon'; import scheduleIdleTask from 'flavours/glitch/util/schedule_idle_task'; +import classNames from 'classnames'; export default class DetailedStatus extends ImmutablePureComponent { @@ -30,6 +31,7 @@ export default class DetailedStatus extends ImmutablePureComponent { measureHeight: PropTypes.bool, onHeightChange: PropTypes.func, domain: PropTypes.string.isRequired, + compact: PropTypes.bool, }; state = { @@ -60,7 +62,7 @@ export default class DetailedStatus extends ImmutablePureComponent { _measureHeight (heightJustChanged) { if (this.props.measureHeight && this.node) { - scheduleIdleTask(() => this.node && this.setState({ height: this.node.offsetHeight })); + scheduleIdleTask(() => this.node && this.setState({ height: this.node.scrollHeight })); if (this.props.onHeightChange && heightJustChanged) { this.props.onHeightChange(); @@ -95,6 +97,7 @@ export default class DetailedStatus extends ImmutablePureComponent { const status = this.props.status.get('reblog') ? this.props.status.get('reblog') : this.props.status; const { expanded, onToggleHidden, settings } = this.props; const outerStyle = { boxSizing: 'border-box' }; + const { compact } = this.props; if (!status) { return null; @@ -200,26 +203,28 @@ export default class DetailedStatus extends ImmutablePureComponent { } return ( -
- -
- -
+
+
+ +
+ +
+ + - - -
- - - {applicationLink} · {reblogLink} · {favouriteLink} · +
+ + + {applicationLink} · {reblogLink} · {favouriteLink} · +
); diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js index aa508c483..86c4db283 100644 --- a/app/javascript/flavours/glitch/features/status/index.js +++ b/app/javascript/flavours/glitch/features/status/index.js @@ -100,6 +100,7 @@ const makeMapStateToProps = () => { descendantsIds, settings: state.get('local_settings'), askReplyConfirmation: state.getIn(['local_settings', 'confirm_before_clearing_draft']) && state.getIn(['compose', 'text']).trim().length !== 0, + domain: state.getIn(['meta', 'domain']), }; }; @@ -123,6 +124,7 @@ export default class Status extends ImmutablePureComponent { descendantsIds: ImmutablePropTypes.list, intl: PropTypes.object.isRequired, askReplyConfirmation: PropTypes.bool, + domain: PropTypes.string.isRequired, }; state = { @@ -417,7 +419,7 @@ export default class Status extends ImmutablePureComponent { render () { let ancestors, descendants; const { setExpansion } = this; - const { status, settings, ancestorsIds, descendantsIds, intl } = this.props; + const { status, settings, ancestorsIds, descendantsIds, intl, domain } = this.props; const { fullscreen, isExpanded } = this.state; if (status === null) { @@ -470,6 +472,7 @@ export default class Status extends ImmutablePureComponent { onOpenMedia={this.handleOpenMedia} expanded={isExpanded} onToggleHidden={this.handleExpandedToggle} + domain={domain} />