diff options
author | Eugen Rochko <eugen@zeonfederated.com> | 2018-06-01 19:18:37 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-06-01 19:18:37 +0200 |
commit | 73c0c36e7be8a543e4d6b326a22dcbfa9d5b566d (patch) | |
tree | 6d9b2ac5deb3fbe4f9beb38571c3ec24a7bda485 | |
parent | 69b45350fe680ef5491eb8cacba92770b04ca1dd (diff) |
Improve trends layout (#7700)
* Allow collapsing trends, responsively hide trends * Add trends column
8 files changed, 124 insertions, 8 deletions
diff --git a/app/javascript/mastodon/features/getting_started/components/trends.js b/app/javascript/mastodon/features/getting_started/components/trends.js index 5d6b7ed8c..96a646bea 100644 --- a/app/javascript/mastodon/features/getting_started/components/trends.js +++ b/app/javascript/mastodon/features/getting_started/components/trends.js @@ -5,6 +5,7 @@ import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { FormattedMessage, defineMessages } from 'react-intl'; import Hashtag from '../../../components/hashtag'; +import { Link } from 'react-router-dom'; const messages = defineMessages({ refresh_trends: { id: 'trends.refresh', defaultMessage: 'Refresh' }, @@ -19,6 +20,9 @@ export default class Trends extends ImmutablePureComponent { static propTypes = { trends: ImmutablePropTypes.list, loading: PropTypes.bool.isRequired, + showTrends: PropTypes.bool.isRequired, + fetchTrends: PropTypes.func.isRequired, + toggleTrends: PropTypes.func.isRequired, }; componentDidMount () { @@ -29,8 +33,12 @@ export default class Trends extends ImmutablePureComponent { this.props.fetchTrends(); } + handleToggle = () => { + this.props.toggleTrends(!this.props.showTrends); + } + render () { - const { intl, trends, loading } = this.props; + const { intl, trends, loading, showTrends } = this.props; if (!trends || trends.size < 1) { return null; @@ -44,13 +52,18 @@ export default class Trends extends ImmutablePureComponent { <i className='fa fa-fire fa-fw' /> <FormattedMessage id='trends.header' defaultMessage='Trending now' /> </button> + <div className='column-header__buttons'> - <button onClick={this.handleRefreshTrends} className='column-header__button' title={intl.formatMessage(messages.refresh_trends)} aria-label={intl.formatMessage(messages.refresh_trends)} disabled={loading}><i className={classNames('fa', 'fa-refresh', { 'fa-spin': loading })} /></button> + {showTrends && <button onClick={this.handleRefreshTrends} className='column-header__button' title={intl.formatMessage(messages.refresh_trends)} aria-label={intl.formatMessage(messages.refresh_trends)} disabled={loading}><i className={classNames('fa', 'fa-refresh', { 'fa-spin': loading })} /></button>} + <button onClick={this.handleToggle} className='column-header__button'><i className={classNames('fa', showTrends ? 'fa-chevron-down' : 'fa-chevron-up')} /></button> </div> </h1> </div> - <div className='getting-started__scrollable'>{trends.take(3).map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)}</div> + {showTrends && <div className='getting-started__scrollable'> + {trends.take(3).map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)} + <Link to='/trends' className='load-more'><FormattedMessage id='status.load_more' defaultMessage='Load more' /></Link> + </div>} </div> ); } diff --git a/app/javascript/mastodon/features/getting_started/containers/trends_container.js b/app/javascript/mastodon/features/getting_started/containers/trends_container.js index 549556b76..65faeae86 100644 --- a/app/javascript/mastodon/features/getting_started/containers/trends_container.js +++ b/app/javascript/mastodon/features/getting_started/containers/trends_container.js @@ -2,14 +2,17 @@ import { connect } from 'react-redux'; import { injectIntl } from 'react-intl'; import { fetchTrends } from '../../../actions/trends'; import Trends from '../components/trends'; +import { changeSetting } from '../../../actions/settings'; const mapStateToProps = state => ({ trends: state.getIn(['trends', 'items']), loading: state.getIn(['trends', 'isLoading']), + showTrends: state.getIn(['settings', 'trends', 'show']), }); const mapDispatchToProps = dispatch => ({ fetchTrends: () => dispatch(fetchTrends()), + toggleTrends: show => dispatch(changeSetting(['trends', 'show'], show)), }); export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(Trends)); diff --git a/app/javascript/mastodon/features/getting_started/index.js b/app/javascript/mastodon/features/getting_started/index.js index 9575afd7a..67a5b282a 100644 --- a/app/javascript/mastodon/features/getting_started/index.js +++ b/app/javascript/mastodon/features/getting_started/index.js @@ -80,6 +80,7 @@ export default class GettingStarted extends ImmutablePureComponent { const navItems = []; let i = 1; + let height = 0; if (multiColumn) { navItems.push( @@ -88,6 +89,8 @@ export default class GettingStarted extends ImmutablePureComponent { <ColumnLink key={i++} icon='globe' text={intl.formatMessage(messages.public_timeline)} to='/timelines/public' />, <ColumnSubheading key={i++} text={intl.formatMessage(messages.personal)} /> ); + + height += 34*2 + 48*2; } navItems.push( @@ -96,8 +99,11 @@ export default class GettingStarted extends ImmutablePureComponent { <ColumnLink key={i++} icon='bars' text={intl.formatMessage(messages.lists)} to='/lists' /> ); + height += 48*3; + if (myAccount.get('locked')) { navItems.push(<ColumnLink key={i++} icon='users' text={intl.formatMessage(messages.follow_requests)} badge={badgeDisplay(unreadFollowRequests, 40)} to='/follow_requests' />); + height += 48; } if (!multiColumn) { @@ -106,6 +112,8 @@ export default class GettingStarted extends ImmutablePureComponent { <ColumnLink key={i++} icon='gears' text={intl.formatMessage(messages.preferences)} href='/settings/preferences' />, <ColumnLink key={i++} icon='lock' text={intl.formatMessage(messages.security)} href='/auth/edit' /> ); + + height += 34 + 48*2; } return ( @@ -119,7 +127,7 @@ export default class GettingStarted extends ImmutablePureComponent { </h1> </div>} - <div className='getting-started__wrapper'> + <div className='getting-started__wrapper' style={{ height }}> {!multiColumn && <NavigationBar account={myAccount} />} {navItems} </div> diff --git a/app/javascript/mastodon/features/trends/index.js b/app/javascript/mastodon/features/trends/index.js new file mode 100644 index 000000000..f33af3e2e --- /dev/null +++ b/app/javascript/mastodon/features/trends/index.js @@ -0,0 +1,66 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import { connect } from 'react-redux'; +import { injectIntl, defineMessages } from 'react-intl'; +import Column from '../ui/components/column'; +import ColumnHeader from '../../components/column_header'; +import Hashtag from '../../components/hashtag'; +import classNames from 'classnames'; +import { fetchTrends } from '../../actions/trends'; + +const messages = defineMessages({ + title: { id: 'trends.header', defaultMessage: 'Trending now' }, + refreshTrends: { id: 'trends.refresh', defaultMessage: 'Refresh trends' }, +}); + +const mapStateToProps = state => ({ + trends: state.getIn(['trends', 'items']), + loading: state.getIn(['trends', 'isLoading']), +}); + +const mapDispatchToProps = dispatch => ({ + fetchTrends: () => dispatch(fetchTrends()), +}); + +@connect(mapStateToProps, mapDispatchToProps) +@injectIntl +export default class Trends extends ImmutablePureComponent { + + static propTypes = { + intl: PropTypes.object.isRequired, + trends: ImmutablePropTypes.list, + fetchTrends: PropTypes.func.isRequired, + loading: PropTypes.bool, + }; + + componentDidMount () { + this.props.fetchTrends(); + } + + handleRefresh = () => { + this.props.fetchTrends(); + } + + render () { + const { trends, loading, intl } = this.props; + + return ( + <Column> + <ColumnHeader + icon='fire' + title={intl.formatMessage(messages.title)} + extraButton={( + <button className='column-header__button' title={intl.formatMessage(messages.refreshTrends)} aria-label={intl.formatMessage(messages.refreshTrends)} onClick={this.handleRefresh}><i className={classNames('fa', 'fa-refresh', { 'fa-spin': loading })} /></button> + )} + /> + + <div className='scrollable'> + {trends && trends.map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)} + </div> + </Column> + ); + } + +} diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js index f1409b946..bfed02f98 100644 --- a/app/javascript/mastodon/features/ui/index.js +++ b/app/javascript/mastodon/features/ui/index.js @@ -42,6 +42,7 @@ import { Mutes, PinnedStatuses, Lists, + Trends, } from './util/async-components'; import { HotKeys } from 'react-hotkeys'; import { me } from '../../initial_state'; @@ -154,6 +155,7 @@ class SwitchingColumnsArea extends React.PureComponent { <WrappedRoute path='/pinned' component={PinnedStatuses} content={children} /> <WrappedRoute path='/search' component={Compose} content={children} componentParams={{ isSearchPage: true }} /> + <WrappedRoute path='/trends' component={Trends} content={children} /> <WrappedRoute path='/statuses/new' component={Compose} content={children} /> <WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} /> diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js index 8cf2a6e7d..dfc796a09 100644 --- a/app/javascript/mastodon/features/ui/util/async-components.js +++ b/app/javascript/mastodon/features/ui/util/async-components.js @@ -129,3 +129,7 @@ export function EmbedModal () { export function ListEditor () { return import(/* webpackChunkName: "features/list_editor" */'../../list_editor'); } + +export function Trends () { + return import(/* webpackChunkName: "features/trends" */'../../trends'); +} diff --git a/app/javascript/mastodon/reducers/settings.js b/app/javascript/mastodon/reducers/settings.js index de8865e43..0a034d739 100644 --- a/app/javascript/mastodon/reducers/settings.js +++ b/app/javascript/mastodon/reducers/settings.js @@ -64,6 +64,10 @@ const initialState = ImmutableMap({ body: '', }), }), + + trends: ImmutableMap({ + show: true, + }), }); const defaultColumns = fromJS([ diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 124998a48..39eaf5061 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2178,8 +2178,7 @@ a.account__display-name { } .getting-started__wrapper { - position: relative; - overflow-y: auto; + flex: 0 0 auto; } .flex-spacer { @@ -2187,7 +2186,6 @@ a.account__display-name { } .getting-started { - flex: 1 0 auto; color: $dark-text-color; p { @@ -2228,7 +2226,23 @@ a.account__display-name { &__trends { background: $ui-base-color; - flex: 1 1 auto; + flex: 0 1 auto; + + @media screen and (max-height: 810px) { + .trends__item:nth-child(3) { + display: none; + } + } + + @media screen and (max-height: 720px) { + .trends__item:nth-child(2) { + display: none; + } + } + + @media screen and (max-height: 670px) { + display: none; + } } &__scrollable { @@ -2460,8 +2474,10 @@ a.status-card { line-height: inherit; margin: 0; padding: 15px; + box-sizing: border-box; width: 100%; clear: both; + text-decoration: none; &:hover { background: lighten($ui-base-color, 2%); |