Add listing of followed hashtags (#21773)
* Add followed_tags route. This at least gets us to the point where the page can actually be rendered, although it doesn't display any hashtags (yet?). Attempting to implement #20763. * Fix minor issues. * I've got the followed tags data partially working But the Hashtag component errors for some reason. Something about the value of the history attribute being invalid. * Fix a mistake in the code * Minor change. * Get the followed hashtags list fully working. Still need to add the Follow/Unfollow buttons, though. * Resolve JS linter issues. * Add pagination logic to followed tags list view. However, it currently loads further pages immediately on page load, so that's not ideal. Need to figure that one out. * Appease the linter. * Apply suggestions from code review Co-authored-by: Claire <claire.github-309c@sitedethib.com> * Fixes and resolve some other feedback. * Use set/update instead of setIn/updateIn. Co-authored-by: Claire <claire.github-309c@sitedethib.com>local
parent
3970a6f433
commit
30e895299c
11 changed files with 231 additions and 2 deletions
@ -0,0 +1,89 @@ |
||||
import { debounce } from 'lodash'; |
||||
import PropTypes from 'prop-types'; |
||||
import React from 'react'; |
||||
import ImmutablePureComponent from 'react-immutable-pure-component'; |
||||
import ImmutablePropTypes from 'react-immutable-proptypes'; |
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; |
||||
import { connect } from 'react-redux'; |
||||
import ColumnHeader from 'mastodon/components/column_header'; |
||||
import ScrollableList from 'mastodon/components/scrollable_list'; |
||||
import Column from 'mastodon/features/ui/components/column'; |
||||
import { Helmet } from 'react-helmet'; |
||||
import Hashtag from 'mastodon/components/hashtag'; |
||||
import { expandFollowedHashtags, fetchFollowedHashtags } from 'mastodon/actions/tags'; |
||||
|
||||
const messages = defineMessages({ |
||||
heading: { id: 'followed_tags', defaultMessage: 'Followed hashtags' }, |
||||
}); |
||||
|
||||
const mapStateToProps = state => ({ |
||||
hashtags: state.getIn(['followed_tags', 'items']), |
||||
isLoading: state.getIn(['followed_tags', 'isLoading'], true), |
||||
hasMore: !!state.getIn(['followed_tags', 'next']), |
||||
}); |
||||
|
||||
export default @connect(mapStateToProps) |
||||
@injectIntl |
||||
class FollowedTags extends ImmutablePureComponent { |
||||
|
||||
static propTypes = { |
||||
params: PropTypes.object.isRequired, |
||||
dispatch: PropTypes.func.isRequired, |
||||
intl: PropTypes.object.isRequired, |
||||
hashtags: ImmutablePropTypes.list, |
||||
isLoading: PropTypes.bool, |
||||
hasMore: PropTypes.bool, |
||||
multiColumn: PropTypes.bool, |
||||
}; |
||||
|
||||
componentDidMount() { |
||||
this.props.dispatch(fetchFollowedHashtags()); |
||||
}; |
||||
|
||||
handleLoadMore = debounce(() => { |
||||
this.props.dispatch(expandFollowedHashtags()); |
||||
}, 300, { leading: true }); |
||||
|
||||
render () { |
||||
const { intl, hashtags, isLoading, hasMore, multiColumn } = this.props; |
||||
|
||||
const emptyMessage = <FormattedMessage id='empty_column.followed_tags' defaultMessage='You have not followed any hashtags yet. When you do, they will show up here.' />; |
||||
|
||||
return ( |
||||
<Column bindToDocument={!multiColumn}> |
||||
<ColumnHeader |
||||
icon='hashtag' |
||||
title={intl.formatMessage(messages.heading)} |
||||
showBackButton |
||||
multiColumn={multiColumn} |
||||
/> |
||||
|
||||
<ScrollableList |
||||
scrollKey='followed_tags' |
||||
emptyMessage={emptyMessage} |
||||
hasMore={hasMore} |
||||
isLoading={isLoading} |
||||
onLoadMore={this.handleLoadMore} |
||||
bindToDocument={!multiColumn} |
||||
> |
||||
{hashtags.map((hashtag) => ( |
||||
<Hashtag |
||||
key={hashtag.get('name')} |
||||
name={hashtag.get('name')} |
||||
to={`/tags/${hashtag.get('name')}`} |
||||
withGraph={false} |
||||
// Taken from ImmutableHashtag. Should maybe refactor ImmutableHashtag to accept more options?
|
||||
people={hashtag.getIn(['history', 0, 'accounts']) * 1 + hashtag.getIn(['history', 1, 'accounts']) * 1} |
||||
history={hashtag.get('history').reverse().map((day) => day.get('uses')).toArray()} |
||||
/> |
||||
))} |
||||
</ScrollableList> |
||||
|
||||
<Helmet> |
||||
<meta name='robots' content='noindex' /> |
||||
</Helmet> |
||||
</Column> |
||||
); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,42 @@ |
||||
import { |
||||
FOLLOWED_HASHTAGS_FETCH_REQUEST, |
||||
FOLLOWED_HASHTAGS_FETCH_SUCCESS, |
||||
FOLLOWED_HASHTAGS_FETCH_FAIL, |
||||
FOLLOWED_HASHTAGS_EXPAND_REQUEST, |
||||
FOLLOWED_HASHTAGS_EXPAND_SUCCESS, |
||||
FOLLOWED_HASHTAGS_EXPAND_FAIL, |
||||
} from 'mastodon/actions/tags'; |
||||
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; |
||||
|
||||
const initialState = ImmutableMap({ |
||||
items: ImmutableList(), |
||||
isLoading: false, |
||||
next: null, |
||||
}); |
||||
|
||||
export default function followed_tags(state = initialState, action) { |
||||
switch(action.type) { |
||||
case FOLLOWED_HASHTAGS_FETCH_REQUEST: |
||||
return state.set('isLoading', true); |
||||
case FOLLOWED_HASHTAGS_FETCH_SUCCESS: |
||||
return state.withMutations(map => { |
||||
map.set('items', fromJS(action.followed_tags)); |
||||
map.set('isLoading', false); |
||||
map.set('next', action.next); |
||||
}); |
||||
case FOLLOWED_HASHTAGS_FETCH_FAIL: |
||||
return state.set('isLoading', false); |
||||
case FOLLOWED_HASHTAGS_EXPAND_REQUEST: |
||||
return state.set('isLoading', true); |
||||
case FOLLOWED_HASHTAGS_EXPAND_SUCCESS: |
||||
return state.withMutations(map => { |
||||
map.update('items', set => set.concat(fromJS(action.followed_tags))); |
||||
map.set('isLoading', false); |
||||
map.set('next', action.next); |
||||
}); |
||||
case FOLLOWED_HASHTAGS_EXPAND_FAIL: |
||||
return state.set('isLoading', false); |
||||
default: |
||||
return state; |
||||
} |
||||
}; |
Loading…
Reference in new issue