From 69b45350fe680ef5491eb8cacba92770b04ca1dd Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Fri, 1 Jun 2018 21:22:42 +0900 Subject: [PATCH] Add loading indicator for trending tags (#7693) --- .../containers/search_results_container.js | 2 +- .../getting_started/components/trends.js | 58 +++++++++++++++++++ .../containers/trends_container.js | 15 +++++ .../features/getting_started/index.js | 31 +--------- .../mastodon/locales/defaultMessages.json | 21 ++++--- app/javascript/mastodon/reducers/trends.js | 18 ++++-- 6 files changed, 104 insertions(+), 41 deletions(-) create mode 100644 app/javascript/mastodon/features/getting_started/components/trends.js create mode 100644 app/javascript/mastodon/features/getting_started/containers/trends_container.js 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 ( +
+
+

+ +
+ +
+

+
+ +
{trends.take(3).map(hashtag => )}
+
+ ); + } + +} 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} - {multiColumn && trends &&
-
-

- -
- -
-

-
- -
{trends.take(3).map(hashtag => )}
-
} + {multiColumn && } {!multiColumn &&
} 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 @@ -1036,6 +1036,19 @@ ], "path": "app/javascript/mastodon/features/follow_requests/index.json" }, + { + "descriptors": [ + { + "defaultMessage": "Refresh", + "id": "trends.refresh" + }, + { + "defaultMessage": "Trending now", + "id": "trends.header" + } + ], + "path": "app/javascript/mastodon/features/getting_started/components/trends.json" + }, { "descriptors": [ { @@ -1094,10 +1107,6 @@ "defaultMessage": "Lists", "id": "navigation_bar.lists" }, - { - "defaultMessage": "Refresh", - "id": "trends.refresh" - }, { "defaultMessage": "Discover", "id": "navigation_bar.discover" @@ -1114,10 +1123,6 @@ "defaultMessage": "Getting started", "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; }