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>
This commit is contained in:
		
							parent
							
								
									3970a6f433
								
							
						
					
					
						commit
						30e895299c
					
				| @ -1,9 +1,17 @@ | |||||||
| import api from '../api'; | import api, { getLinks } from '../api'; | ||||||
| 
 | 
 | ||||||
| export const HASHTAG_FETCH_REQUEST = 'HASHTAG_FETCH_REQUEST'; | export const HASHTAG_FETCH_REQUEST = 'HASHTAG_FETCH_REQUEST'; | ||||||
| export const HASHTAG_FETCH_SUCCESS = 'HASHTAG_FETCH_SUCCESS'; | export const HASHTAG_FETCH_SUCCESS = 'HASHTAG_FETCH_SUCCESS'; | ||||||
| export const HASHTAG_FETCH_FAIL    = 'HASHTAG_FETCH_FAIL'; | export const HASHTAG_FETCH_FAIL    = 'HASHTAG_FETCH_FAIL'; | ||||||
| 
 | 
 | ||||||
|  | export const FOLLOWED_HASHTAGS_FETCH_REQUEST = 'FOLLOWED_HASHTAGS_FETCH_REQUEST'; | ||||||
|  | export const FOLLOWED_HASHTAGS_FETCH_SUCCESS = 'FOLLOWED_HASHTAGS_FETCH_SUCCESS'; | ||||||
|  | export const FOLLOWED_HASHTAGS_FETCH_FAIL    = 'FOLLOWED_HASHTAGS_FETCH_FAIL'; | ||||||
|  | 
 | ||||||
|  | export const FOLLOWED_HASHTAGS_EXPAND_REQUEST = 'FOLLOWED_HASHTAGS_EXPAND_REQUEST'; | ||||||
|  | export const FOLLOWED_HASHTAGS_EXPAND_SUCCESS = 'FOLLOWED_HASHTAGS_EXPAND_SUCCESS'; | ||||||
|  | export const FOLLOWED_HASHTAGS_EXPAND_FAIL    = 'FOLLOWED_HASHTAGS_EXPAND_FAIL'; | ||||||
|  | 
 | ||||||
| export const HASHTAG_FOLLOW_REQUEST = 'HASHTAG_FOLLOW_REQUEST'; | export const HASHTAG_FOLLOW_REQUEST = 'HASHTAG_FOLLOW_REQUEST'; | ||||||
| export const HASHTAG_FOLLOW_SUCCESS = 'HASHTAG_FOLLOW_SUCCESS'; | export const HASHTAG_FOLLOW_SUCCESS = 'HASHTAG_FOLLOW_SUCCESS'; | ||||||
| export const HASHTAG_FOLLOW_FAIL    = 'HASHTAG_FOLLOW_FAIL'; | export const HASHTAG_FOLLOW_FAIL    = 'HASHTAG_FOLLOW_FAIL'; | ||||||
| @ -37,6 +45,78 @@ export const fetchHashtagFail = error => ({ | |||||||
|   error, |   error, | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  | export const fetchFollowedHashtags = () => (dispatch, getState) => { | ||||||
|  |   dispatch(fetchFollowedHashtagsRequest()); | ||||||
|  | 
 | ||||||
|  |   api(getState).get('/api/v1/followed_tags').then(response => { | ||||||
|  |     const next = getLinks(response).refs.find(link => link.rel === 'next'); | ||||||
|  |     dispatch(fetchFollowedHashtagsSuccess(response.data, next ? next.uri : null)); | ||||||
|  |   }).catch(err => { | ||||||
|  |     dispatch(fetchFollowedHashtagsFail(err)); | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function fetchFollowedHashtagsRequest() { | ||||||
|  |   return { | ||||||
|  |     type: FOLLOWED_HASHTAGS_FETCH_REQUEST, | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function fetchFollowedHashtagsSuccess(followed_tags, next) { | ||||||
|  |   return { | ||||||
|  |     type: FOLLOWED_HASHTAGS_FETCH_SUCCESS, | ||||||
|  |     followed_tags, | ||||||
|  |     next, | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function fetchFollowedHashtagsFail(error) { | ||||||
|  |   return { | ||||||
|  |     type: FOLLOWED_HASHTAGS_FETCH_FAIL, | ||||||
|  |     error, | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function expandFollowedHashtags() { | ||||||
|  |   return (dispatch, getState) => { | ||||||
|  |     const url = getState().getIn(['followed_tags', 'next']); | ||||||
|  | 
 | ||||||
|  |     if (url === null) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     dispatch(expandFollowedHashtagsRequest()); | ||||||
|  | 
 | ||||||
|  |     api(getState).get(url).then(response => { | ||||||
|  |       const next = getLinks(response).refs.find(link => link.rel === 'next'); | ||||||
|  |       dispatch(expandFollowedHashtagsSuccess(response.data, next ? next.uri : null)); | ||||||
|  |     }).catch(error => { | ||||||
|  |       dispatch(expandFollowedHashtagsFail(error)); | ||||||
|  |     }); | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function expandFollowedHashtagsRequest() { | ||||||
|  |   return { | ||||||
|  |     type: FOLLOWED_HASHTAGS_EXPAND_REQUEST, | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function expandFollowedHashtagsSuccess(followed_tags, next) { | ||||||
|  |   return { | ||||||
|  |     type: FOLLOWED_HASHTAGS_EXPAND_SUCCESS, | ||||||
|  |     followed_tags, | ||||||
|  |     next, | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function expandFollowedHashtagsFail(error) { | ||||||
|  |   return { | ||||||
|  |     type: FOLLOWED_HASHTAGS_EXPAND_FAIL, | ||||||
|  |     error, | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| export const followHashtag = name => (dispatch, getState) => { | export const followHashtag = name => (dispatch, getState) => { | ||||||
|   dispatch(followHashtagRequest(name)); |   dispatch(followHashtagRequest(name)); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -46,6 +46,7 @@ const messages = defineMessages({ | |||||||
|   follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' }, |   follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' }, | ||||||
|   favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' }, |   favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' }, | ||||||
|   lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' }, |   lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' }, | ||||||
|  |   followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed hashtags' }, | ||||||
|   blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' }, |   blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' }, | ||||||
|   domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Blocked domains' }, |   domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Blocked domains' }, | ||||||
|   mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' }, |   mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' }, | ||||||
| @ -242,6 +243,7 @@ class Header extends ImmutablePureComponent { | |||||||
|       menu.push({ text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' }); |       menu.push({ text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' }); | ||||||
|       menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' }); |       menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' }); | ||||||
|       menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' }); |       menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' }); | ||||||
|  |       menu.push({ text: intl.formatMessage(messages.followed_tags), to: '/followed_tags' }); | ||||||
|       menu.push(null); |       menu.push(null); | ||||||
|       menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' }); |       menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' }); | ||||||
|       menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' }); |       menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' }); | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ const messages = defineMessages({ | |||||||
|   follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' }, |   follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' }, | ||||||
|   favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' }, |   favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' }, | ||||||
|   lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' }, |   lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' }, | ||||||
|  |   followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed hashtags' }, | ||||||
|   blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' }, |   blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' }, | ||||||
|   domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' }, |   domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' }, | ||||||
|   mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' }, |   mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' }, | ||||||
| @ -45,6 +46,7 @@ class ActionBar extends React.PureComponent { | |||||||
|     menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' }); |     menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' }); | ||||||
|     menu.push({ text: intl.formatMessage(messages.bookmarks), to: '/bookmarks' }); |     menu.push({ text: intl.formatMessage(messages.bookmarks), to: '/bookmarks' }); | ||||||
|     menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' }); |     menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' }); | ||||||
|  |     menu.push({ text: intl.formatMessage(messages.followed_tags), to: '/followed_tags' }); | ||||||
|     menu.push(null); |     menu.push(null); | ||||||
|     menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' }); |     menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' }); | ||||||
|     menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' }); |     menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' }); | ||||||
|  | |||||||
							
								
								
									
										89
									
								
								app/javascript/mastodon/features/followed_tags/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								app/javascript/mastodon/features/followed_tags/index.js
									
									
									
									
									
										Normal file
									
								
							| @ -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> | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -42,6 +42,7 @@ import { | |||||||
|   FollowRequests, |   FollowRequests, | ||||||
|   FavouritedStatuses, |   FavouritedStatuses, | ||||||
|   BookmarkedStatuses, |   BookmarkedStatuses, | ||||||
|  |   FollowedTags, | ||||||
|   ListTimeline, |   ListTimeline, | ||||||
|   Blocks, |   Blocks, | ||||||
|   DomainBlocks, |   DomainBlocks, | ||||||
| @ -216,6 +217,7 @@ class SwitchingColumnsArea extends React.PureComponent { | |||||||
|           <WrappedRoute path='/follow_requests' component={FollowRequests} content={children} /> |           <WrappedRoute path='/follow_requests' component={FollowRequests} content={children} /> | ||||||
|           <WrappedRoute path='/blocks' component={Blocks} content={children} /> |           <WrappedRoute path='/blocks' component={Blocks} content={children} /> | ||||||
|           <WrappedRoute path='/domain_blocks' component={DomainBlocks} content={children} /> |           <WrappedRoute path='/domain_blocks' component={DomainBlocks} content={children} /> | ||||||
|  |           <WrappedRoute path='/followed_tags' component={FollowedTags} content={children} /> | ||||||
|           <WrappedRoute path='/mutes' component={Mutes} content={children} /> |           <WrappedRoute path='/mutes' component={Mutes} content={children} /> | ||||||
|           <WrappedRoute path='/lists' component={Lists} content={children} /> |           <WrappedRoute path='/lists' component={Lists} content={children} /> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -90,6 +90,10 @@ export function FavouritedStatuses () { | |||||||
|   return import(/* webpackChunkName: "features/favourited_statuses" */'../../favourited_statuses'); |   return import(/* webpackChunkName: "features/favourited_statuses" */'../../favourited_statuses'); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export function FollowedTags () { | ||||||
|  |   return import(/* webpackChunkName: "features/followed_tags" */'../../followed_tags'); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export function BookmarkedStatuses () { | export function BookmarkedStatuses () { | ||||||
|   return import(/* webpackChunkName: "features/bookmarked_statuses" */'../../bookmarked_statuses'); |   return import(/* webpackChunkName: "features/bookmarked_statuses" */'../../bookmarked_statuses'); | ||||||
| } | } | ||||||
|  | |||||||
| @ -1391,6 +1391,10 @@ | |||||||
|         "defaultMessage": "Lists", |         "defaultMessage": "Lists", | ||||||
|         "id": "navigation_bar.lists" |         "id": "navigation_bar.lists" | ||||||
|       }, |       }, | ||||||
|  |       { | ||||||
|  |         "defaultMessage": "Followed hashtags", | ||||||
|  |         "id": "navigation_bar.followed_tags" | ||||||
|  |       }, | ||||||
|       { |       { | ||||||
|         "defaultMessage": "Blocked users", |         "defaultMessage": "Blocked users", | ||||||
|         "id": "navigation_bar.blocks" |         "id": "navigation_bar.blocks" | ||||||
| @ -4310,4 +4314,4 @@ | |||||||
|     ], |     ], | ||||||
|     "path": "app/javascript/mastodon/features/video/index.json" |     "path": "app/javascript/mastodon/features/video/index.json" | ||||||
|   } |   } | ||||||
| ] | ] | ||||||
|  | |||||||
| @ -379,6 +379,7 @@ | |||||||
|   "navigation_bar.favourites": "Favourites", |   "navigation_bar.favourites": "Favourites", | ||||||
|   "navigation_bar.filters": "Muted words", |   "navigation_bar.filters": "Muted words", | ||||||
|   "navigation_bar.follow_requests": "Follow requests", |   "navigation_bar.follow_requests": "Follow requests", | ||||||
|  |   "navigation_bar.followed_tags": "Followed hashtags", | ||||||
|   "navigation_bar.follows_and_followers": "Follows and followers", |   "navigation_bar.follows_and_followers": "Follows and followers", | ||||||
|   "navigation_bar.lists": "Lists", |   "navigation_bar.lists": "Lists", | ||||||
|   "navigation_bar.logout": "Logout", |   "navigation_bar.logout": "Logout", | ||||||
|  | |||||||
							
								
								
									
										42
									
								
								app/javascript/mastodon/reducers/followed_tags.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								app/javascript/mastodon/reducers/followed_tags.js
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||||
|  |   } | ||||||
|  | }; | ||||||
| @ -40,6 +40,7 @@ import picture_in_picture from './picture_in_picture'; | |||||||
| import accounts_map from './accounts_map'; | import accounts_map from './accounts_map'; | ||||||
| import history from './history'; | import history from './history'; | ||||||
| import tags from './tags'; | import tags from './tags'; | ||||||
|  | import followed_tags from './followed_tags'; | ||||||
| 
 | 
 | ||||||
| const reducers = { | const reducers = { | ||||||
|   announcements, |   announcements, | ||||||
| @ -83,6 +84,7 @@ const reducers = { | |||||||
|   picture_in_picture, |   picture_in_picture, | ||||||
|   history, |   history, | ||||||
|   tags, |   tags, | ||||||
|  |   followed_tags, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default combineReducers(reducers); | export default combineReducers(reducers); | ||||||
|  | |||||||
| @ -27,6 +27,7 @@ Rails.application.routes.draw do | |||||||
|     /blocks |     /blocks | ||||||
|     /domain_blocks |     /domain_blocks | ||||||
|     /mutes |     /mutes | ||||||
|  |     /followed_tags | ||||||
|     /statuses/(*any) |     /statuses/(*any) | ||||||
|   ).freeze |   ).freeze | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user