about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2018-06-01 19:18:37 +0200
committerGitHub <noreply@github.com>2018-06-01 19:18:37 +0200
commit73c0c36e7be8a543e4d6b326a22dcbfa9d5b566d (patch)
tree6d9b2ac5deb3fbe4f9beb38571c3ec24a7bda485
parent69b45350fe680ef5491eb8cacba92770b04ca1dd (diff)
Improve trends layout (#7700)
* Allow collapsing trends, responsively hide trends

* Add trends column
-rw-r--r--app/javascript/mastodon/features/getting_started/components/trends.js19
-rw-r--r--app/javascript/mastodon/features/getting_started/containers/trends_container.js3
-rw-r--r--app/javascript/mastodon/features/getting_started/index.js10
-rw-r--r--app/javascript/mastodon/features/trends/index.js66
-rw-r--r--app/javascript/mastodon/features/ui/index.js2
-rw-r--r--app/javascript/mastodon/features/ui/util/async-components.js4
-rw-r--r--app/javascript/mastodon/reducers/settings.js4
-rw-r--r--app/javascript/styles/mastodon/components.scss24
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%);