about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
authorYamagishi Kazutoshi <ykzts@desire.sh>2018-06-01 21:22:42 +0900
committerEugen Rochko <eugen@zeonfederated.com>2018-06-01 14:22:42 +0200
commit69b45350fe680ef5491eb8cacba92770b04ca1dd (patch)
tree4e6a1cd04f36ebd49baa379adff321825b64a5ee /app
parentbfa12239e84ebe310d74ef0e773d90a477b3cf8f (diff)
Add loading indicator for trending tags (#7693)
Diffstat (limited to 'app')
-rw-r--r--app/javascript/mastodon/features/compose/containers/search_results_container.js2
-rw-r--r--app/javascript/mastodon/features/getting_started/components/trends.js58
-rw-r--r--app/javascript/mastodon/features/getting_started/containers/trends_container.js15
-rw-r--r--app/javascript/mastodon/features/getting_started/index.js31
-rw-r--r--app/javascript/mastodon/locales/defaultMessages.json21
-rw-r--r--app/javascript/mastodon/reducers/trends.js18
6 files changed, 104 insertions, 41 deletions
diff --git a/app/javascript/mastodon/features/compose/containers/search_results_container.js b/app/javascript/mastodon/features/compose/containers/search_results_container.js
index 7273460e2..2f879f9d9 100644
--- a/app/javascript/mastodon/features/compose/containers/search_results_container.js
+++ b/app/javascript/mastodon/features/compose/containers/search_results_container.js
@@ -4,7 +4,7 @@ import { fetchTrends } from '../../../actions/trends';
 
 const mapStateToProps = state => ({
   results: state.getIn(['search', 'results']),
-  trends: state.get('trends'),
+  trends: state.getIn(['trends', 'items']),
 });
 
 const mapDispatchToProps = dispatch => ({
diff --git a/app/javascript/mastodon/features/getting_started/components/trends.js b/app/javascript/mastodon/features/getting_started/components/trends.js
new file mode 100644
index 000000000..5d6b7ed8c
--- /dev/null
+++ b/app/javascript/mastodon/features/getting_started/components/trends.js
@@ -0,0 +1,58 @@
+import classNames from 'classnames';
+import React from 'react';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import PropTypes from 'prop-types';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import { FormattedMessage, defineMessages } from 'react-intl';
+import Hashtag from '../../../components/hashtag';
+
+const messages = defineMessages({
+  refresh_trends: { id: 'trends.refresh', defaultMessage: 'Refresh' },
+});
+
+export default class Trends extends ImmutablePureComponent {
+
+  static defaultProps = {
+    loading: false,
+  };
+
+  static propTypes = {
+    trends: ImmutablePropTypes.list,
+    loading: PropTypes.bool.isRequired,
+  };
+
+  componentDidMount () {
+    setTimeout(() => this.props.fetchTrends(), 5000);
+  }
+
+  handleRefreshTrends = () => {
+    this.props.fetchTrends();
+  }
+
+  render () {
+    const { intl, trends, loading } = this.props;
+
+    if (!trends || trends.size < 1) {
+      return null;
+    }
+
+    return (
+      <div className='getting-started__trends'>
+        <div className='column-header__wrapper'>
+          <h1 className='column-header'>
+            <button>
+              <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>
+            </div>
+          </h1>
+        </div>
+
+        <div className='getting-started__scrollable'>{trends.take(3).map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)}</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
new file mode 100644
index 000000000..549556b76
--- /dev/null
+++ b/app/javascript/mastodon/features/getting_started/containers/trends_container.js
@@ -0,0 +1,15 @@
+import { connect } from 'react-redux';
+import { injectIntl } from 'react-intl';
+import { fetchTrends } from '../../../actions/trends';
+import Trends from '../components/trends';
+
+const mapStateToProps = state => ({
+  trends: state.getIn(['trends', 'items']),
+  loading: state.getIn(['trends', 'isLoading']),
+});
+
+const mapDispatchToProps = dispatch => ({
+  fetchTrends: () => dispatch(fetchTrends()),
+});
+
+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 da1909653..9575afd7a 100644
--- a/app/javascript/mastodon/features/getting_started/index.js
+++ b/app/javascript/mastodon/features/getting_started/index.js
@@ -11,9 +11,8 @@ import { me } from '../../initial_state';
 import { fetchFollowRequests } from '../../actions/accounts';
 import { List as ImmutableList } from 'immutable';
 import { Link } from 'react-router-dom';
-import { fetchTrends } from '../../actions/trends';
-import Hashtag from '../../components/hashtag';
 import NavigationBar from '../compose/components/navigation_bar';
+import TrendsContainer from './containers/trends_container';
 
 const messages = defineMessages({
   home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' },
@@ -30,7 +29,6 @@ const messages = defineMessages({
   mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
   pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' },
   lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
-  refresh_trends: { id: 'trends.refresh', defaultMessage: 'Refresh' },
   discover: { id: 'navigation_bar.discover', defaultMessage: 'Discover' },
   personal: { id: 'navigation_bar.personal', defaultMessage: 'Personal' },
   security: { id: 'navigation_bar.security', defaultMessage: 'Security' },
@@ -39,12 +37,10 @@ const messages = defineMessages({
 const mapStateToProps = state => ({
   myAccount: state.getIn(['accounts', me]),
   unreadFollowRequests: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableList()).size,
-  trends: state.get('trends'),
 });
 
 const mapDispatchToProps = dispatch => ({
   fetchFollowRequests: () => dispatch(fetchFollowRequests()),
-  fetchTrends: () => dispatch(fetchTrends()),
 });
 
 const badgeDisplay = (number, limit) => {
@@ -69,7 +65,6 @@ export default class GettingStarted extends ImmutablePureComponent {
     fetchFollowRequests: PropTypes.func.isRequired,
     unreadFollowRequests: PropTypes.number,
     unreadNotifications: PropTypes.number,
-    trends: ImmutablePropTypes.list,
   };
 
   componentDidMount () {
@@ -78,16 +73,10 @@ export default class GettingStarted extends ImmutablePureComponent {
     if (myAccount.get('locked')) {
       fetchFollowRequests();
     }
-
-    setTimeout(() => this.props.fetchTrends(), 5000);
-  }
-
-  handleRefreshTrends = () => {
-    this.props.fetchTrends();
   }
 
   render () {
-    const { intl, myAccount, multiColumn, unreadFollowRequests, trends } = this.props;
+    const { intl, myAccount, multiColumn, unreadFollowRequests } = this.props;
 
     const navItems = [];
     let i = 1;
@@ -135,21 +124,7 @@ export default class GettingStarted extends ImmutablePureComponent {
           {navItems}
         </div>
 
-        {multiColumn && trends && <div className='getting-started__trends'>
-          <div className='column-header__wrapper'>
-            <h1 className='column-header'>
-              <button>
-                <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)}><i className='fa fa-refresh' /></button>
-              </div>
-            </h1>
-          </div>
-
-          <div className='getting-started__scrollable'>{trends.take(3).map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)}</div>
-        </div>}
+        {multiColumn && <TrendsContainer />}
 
         {!multiColumn && <div className='flex-spacer' />}
 
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
index d8a115a76..c710e1270 100644
--- a/app/javascript/mastodon/locales/defaultMessages.json
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -1039,6 +1039,19 @@
   {
     "descriptors": [
       {
+        "defaultMessage": "Refresh",
+        "id": "trends.refresh"
+      },
+      {
+        "defaultMessage": "Trending now",
+        "id": "trends.header"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/getting_started/components/trends.json"
+  },
+  {
+    "descriptors": [
+      {
         "defaultMessage": "Home",
         "id": "tabs_bar.home"
       },
@@ -1095,10 +1108,6 @@
         "id": "navigation_bar.lists"
       },
       {
-        "defaultMessage": "Refresh",
-        "id": "trends.refresh"
-      },
-      {
         "defaultMessage": "Discover",
         "id": "navigation_bar.discover"
       },
@@ -1115,10 +1124,6 @@
         "id": "getting_started.heading"
       },
       {
-        "defaultMessage": "Trending now",
-        "id": "trends.header"
-      },
-      {
         "defaultMessage": "Hotkeys",
         "id": "navigation_bar.keyboard_shortcuts"
       },
diff --git a/app/javascript/mastodon/reducers/trends.js b/app/javascript/mastodon/reducers/trends.js
index 95cf8f284..5cecc8fca 100644
--- a/app/javascript/mastodon/reducers/trends.js
+++ b/app/javascript/mastodon/reducers/trends.js
@@ -1,12 +1,22 @@
-import { TRENDS_FETCH_SUCCESS } from '../actions/trends';
-import { fromJS } from 'immutable';
+import { TRENDS_FETCH_REQUEST, TRENDS_FETCH_SUCCESS, TRENDS_FETCH_FAIL } from '../actions/trends';
+import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
 
-const initialState = null;
+const initialState = ImmutableMap({
+  items: ImmutableList(),
+  isLoading: false,
+});
 
 export default function trendsReducer(state = initialState, action) {
   switch(action.type) {
+  case TRENDS_FETCH_REQUEST:
+    return state.set('isLoading', true);
   case TRENDS_FETCH_SUCCESS:
-    return fromJS(action.trends);
+    return state.withMutations(map => {
+      map.set('items', fromJS(action.trends));
+      map.set('isLoading', false);
+    });
+  case TRENDS_FETCH_FAIL:
+    return state.set('isLoading', false);
   default:
     return state;
   }