Remove trending hashtags (#7711)
* Delete trends_controller.rb * Update routes.rb * Update trending_tags.rb * Update index.js * Update index.js * Update search_results.js * Update async-components.js * Update index.js * Delete trends.js * Delete trends.js * Delete trends_container.js * Delete trends.js * Update search_results.js * Update search_results_container.js
This commit is contained in:
		
					parent
					
						
							
								00512ecf87
							
						
					
				
			
			
				commit
				
					
						0deb9fa6b9
					
				
			
		
					 13 changed files with 2 additions and 226 deletions
				
			
		|  | @ -1,17 +0,0 @@ | ||||||
| # frozen_string_literal: true |  | ||||||
| 
 |  | ||||||
| class Api::V1::TrendsController < Api::BaseController |  | ||||||
|   before_action :set_tags |  | ||||||
| 
 |  | ||||||
|   respond_to :json |  | ||||||
| 
 |  | ||||||
|   def index |  | ||||||
|     render json: @tags, each_serializer: REST::TagSerializer |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   private |  | ||||||
| 
 |  | ||||||
|   def set_tags |  | ||||||
|     @tags = TrendingTags.get(limit_param(10)) |  | ||||||
|   end |  | ||||||
| end |  | ||||||
|  | @ -1,32 +0,0 @@ | ||||||
| import api from '../api'; |  | ||||||
| 
 |  | ||||||
| export const TRENDS_FETCH_REQUEST = 'TRENDS_FETCH_REQUEST'; |  | ||||||
| export const TRENDS_FETCH_SUCCESS = 'TRENDS_FETCH_SUCCESS'; |  | ||||||
| export const TRENDS_FETCH_FAIL    = 'TRENDS_FETCH_FAIL'; |  | ||||||
| 
 |  | ||||||
| export const fetchTrends = () => (dispatch, getState) => { |  | ||||||
|   dispatch(fetchTrendsRequest()); |  | ||||||
| 
 |  | ||||||
|   api(getState) |  | ||||||
|     .get('/api/v1/trends') |  | ||||||
|     .then(({ data }) => dispatch(fetchTrendsSuccess(data))) |  | ||||||
|     .catch(err => dispatch(fetchTrendsFail(err))); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export const fetchTrendsRequest = () => ({ |  | ||||||
|   type: TRENDS_FETCH_REQUEST, |  | ||||||
|   skipLoading: true, |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| export const fetchTrendsSuccess = trends => ({ |  | ||||||
|   type: TRENDS_FETCH_SUCCESS, |  | ||||||
|   trends, |  | ||||||
|   skipLoading: true, |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| export const fetchTrendsFail = error => ({ |  | ||||||
|   type: TRENDS_FETCH_FAIL, |  | ||||||
|   error, |  | ||||||
|   skipLoading: true, |  | ||||||
|   skipAlert: true, |  | ||||||
| }); |  | ||||||
|  | @ -1,5 +1,4 @@ | ||||||
| import React from 'react'; | import React from 'react'; | ||||||
| import PropTypes from 'prop-types'; |  | ||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import { FormattedMessage } from 'react-intl'; | import { FormattedMessage } from 'react-intl'; | ||||||
| import AccountContainer from '../../../containers/account_container'; | import AccountContainer from '../../../containers/account_container'; | ||||||
|  | @ -11,36 +10,14 @@ export default class SearchResults extends ImmutablePureComponent { | ||||||
| 
 | 
 | ||||||
|   static propTypes = { |   static propTypes = { | ||||||
|     results: ImmutablePropTypes.map.isRequired, |     results: ImmutablePropTypes.map.isRequired, | ||||||
|     trends: ImmutablePropTypes.list, |  | ||||||
|     fetchTrends: PropTypes.func.isRequired, |  | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   componentDidMount () { |  | ||||||
|     const { fetchTrends } = this.props; |  | ||||||
|     fetchTrends(); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   render () { |   render () { | ||||||
|     const { results, trends } = this.props; |     const { results } = this.props; | ||||||
| 
 | 
 | ||||||
|     let accounts, statuses, hashtags; |     let accounts, statuses, hashtags; | ||||||
|     let count = 0; |     let count = 0; | ||||||
| 
 | 
 | ||||||
|     if (results.isEmpty()) { |  | ||||||
|       return ( |  | ||||||
|         <div className='search-results'> |  | ||||||
|           <div className='trends'> |  | ||||||
|             <div className='trends__header'> |  | ||||||
|               <i className='fa fa-fire fa-fw' /> |  | ||||||
|               <FormattedMessage id='trends.header' defaultMessage='Trending now' /> |  | ||||||
|             </div> |  | ||||||
| 
 |  | ||||||
|             {trends && trends.map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)} |  | ||||||
|           </div> |  | ||||||
|         </div> |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (results.get('accounts') && results.get('accounts').size > 0) { |     if (results.get('accounts') && results.get('accounts').size > 0) { | ||||||
|       count   += results.get('accounts').size; |       count   += results.get('accounts').size; | ||||||
|       accounts = ( |       accounts = ( | ||||||
|  |  | ||||||
|  | @ -1,14 +1,8 @@ | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| import SearchResults from '../components/search_results'; | import SearchResults from '../components/search_results'; | ||||||
| import { fetchTrends } from '../../../actions/trends'; |  | ||||||
| 
 | 
 | ||||||
| const mapStateToProps = state => ({ | const mapStateToProps = state => ({ | ||||||
|   results: state.getIn(['search', 'results']), |   results: state.getIn(['search', 'results']), | ||||||
|   trends: state.getIn(['trends', 'items']), |  | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const mapDispatchToProps = dispatch => ({ | export default connect(mapStateToProps)(SearchResults); | ||||||
|   fetchTrends: () => dispatch(fetchTrends()), |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| export default connect(mapStateToProps, mapDispatchToProps)(SearchResults); |  | ||||||
|  |  | ||||||
|  | @ -1,71 +0,0 @@ | ||||||
| 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'; |  | ||||||
| import { Link } from 'react-router-dom'; |  | ||||||
| 
 |  | ||||||
| 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, |  | ||||||
|     showTrends: PropTypes.bool.isRequired, |  | ||||||
|     fetchTrends: PropTypes.func.isRequired, |  | ||||||
|     toggleTrends: PropTypes.func.isRequired, |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   componentDidMount () { |  | ||||||
|     setTimeout(() => this.props.fetchTrends(), 5000); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   handleRefreshTrends = () => { |  | ||||||
|     this.props.fetchTrends(); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   handleToggle = () => { |  | ||||||
|     this.props.toggleTrends(!this.props.showTrends); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   render () { |  | ||||||
|     const { intl, trends, loading, showTrends } = 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'> |  | ||||||
|               {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> |  | ||||||
| 
 |  | ||||||
|         {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> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,18 +0,0 @@ | ||||||
| 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)); |  | ||||||
|  | @ -12,7 +12,6 @@ import { fetchFollowRequests } from '../../actions/accounts'; | ||||||
| import { List as ImmutableList } from 'immutable'; | import { List as ImmutableList } from 'immutable'; | ||||||
| import { Link } from 'react-router-dom'; | import { Link } from 'react-router-dom'; | ||||||
| import NavigationBar from '../compose/components/navigation_bar'; | import NavigationBar from '../compose/components/navigation_bar'; | ||||||
| import TrendsContainer from './containers/trends_container'; |  | ||||||
| 
 | 
 | ||||||
| const messages = defineMessages({ | const messages = defineMessages({ | ||||||
|   home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' }, |   home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' }, | ||||||
|  | @ -132,8 +131,6 @@ export default class GettingStarted extends ImmutablePureComponent { | ||||||
|           {navItems} |           {navItems} | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         {multiColumn && <TrendsContainer />} |  | ||||||
| 
 |  | ||||||
|         {!multiColumn && <div className='flex-spacer' />} |         {!multiColumn && <div className='flex-spacer' />} | ||||||
| 
 | 
 | ||||||
|         <div className='getting-started getting-started__footer'> |         <div className='getting-started getting-started__footer'> | ||||||
|  |  | ||||||
|  | @ -42,7 +42,6 @@ import { | ||||||
|   Mutes, |   Mutes, | ||||||
|   PinnedStatuses, |   PinnedStatuses, | ||||||
|   Lists, |   Lists, | ||||||
|   Trends, |  | ||||||
| } from './util/async-components'; | } from './util/async-components'; | ||||||
| import { HotKeys } from 'react-hotkeys'; | import { HotKeys } from 'react-hotkeys'; | ||||||
| import { me } from '../../initial_state'; | import { me } from '../../initial_state'; | ||||||
|  | @ -155,7 +154,6 @@ class SwitchingColumnsArea extends React.PureComponent { | ||||||
|           <WrappedRoute path='/pinned' component={PinnedStatuses} content={children} /> |           <WrappedRoute path='/pinned' component={PinnedStatuses} content={children} /> | ||||||
| 
 | 
 | ||||||
|           <WrappedRoute path='/search' component={Compose} content={children} componentParams={{ isSearchPage: true }} /> |           <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/new' component={Compose} content={children} /> | ||||||
|           <WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} /> |           <WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} /> | ||||||
|  |  | ||||||
|  | @ -129,7 +129,3 @@ export function EmbedModal () { | ||||||
| export function ListEditor () { | export function ListEditor () { | ||||||
|   return import(/* webpackChunkName: "features/list_editor" */'../../list_editor'); |   return import(/* webpackChunkName: "features/list_editor" */'../../list_editor'); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| export function Trends () { |  | ||||||
|   return import(/* webpackChunkName: "features/trends" */'../../trends'); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -26,7 +26,6 @@ import height_cache from './height_cache'; | ||||||
| import custom_emojis from './custom_emojis'; | import custom_emojis from './custom_emojis'; | ||||||
| import lists from './lists'; | import lists from './lists'; | ||||||
| import listEditor from './list_editor'; | import listEditor from './list_editor'; | ||||||
| import trends from './trends'; |  | ||||||
| 
 | 
 | ||||||
| const reducers = { | const reducers = { | ||||||
|   dropdown_menu, |   dropdown_menu, | ||||||
|  | @ -56,7 +55,6 @@ const reducers = { | ||||||
|   custom_emojis, |   custom_emojis, | ||||||
|   lists, |   lists, | ||||||
|   listEditor, |   listEditor, | ||||||
|   trends, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default combineReducers(reducers); | export default combineReducers(reducers); | ||||||
|  |  | ||||||
|  | @ -1,23 +0,0 @@ | ||||||
| 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 = 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 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; |  | ||||||
|   } |  | ||||||
| }; |  | ||||||
|  | @ -1,9 +1,7 @@ | ||||||
| # frozen_string_literal: true | # frozen_string_literal: true | ||||||
| 
 | 
 | ||||||
| class TrendingTags | class TrendingTags | ||||||
|   KEY                  = 'trending_tags' |  | ||||||
|   EXPIRE_HISTORY_AFTER = 7.days.seconds |   EXPIRE_HISTORY_AFTER = 7.days.seconds | ||||||
|   THRESHOLD            = 5 |  | ||||||
| 
 | 
 | ||||||
|   class << self |   class << self | ||||||
|     def record_use!(tag, account, at_time = Time.now.utc) |     def record_use!(tag, account, at_time = Time.now.utc) | ||||||
|  | @ -11,30 +9,10 @@ class TrendingTags | ||||||
| 
 | 
 | ||||||
|       increment_historical_use!(tag.id, at_time) |       increment_historical_use!(tag.id, at_time) | ||||||
|       increment_unique_use!(tag.id, account.id, at_time) |       increment_unique_use!(tag.id, account.id, at_time) | ||||||
|       increment_vote!(tag.id, at_time) |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     def get(limit) |  | ||||||
|       tag_ids = redis.zrevrange(KEY, 0, limit).map(&:to_i) |  | ||||||
|       tags    = Tag.where(id: tag_ids).to_a.map { |tag| [tag.id, tag] }.to_h |  | ||||||
|       tag_ids.map { |tag_id| tags[tag_id] }.compact |  | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     private |     private | ||||||
| 
 | 
 | ||||||
|     def increment_vote!(tag_id, at_time) |  | ||||||
|       expected = redis.pfcount("activity:tags:#{tag_id}:#{(at_time - 1.day).beginning_of_day.to_i}:accounts").to_f |  | ||||||
|       expected = 1.0 if expected.zero? |  | ||||||
|       observed = redis.pfcount("activity:tags:#{tag_id}:#{at_time.beginning_of_day.to_i}:accounts").to_f |  | ||||||
| 
 |  | ||||||
|       if expected > observed || observed < THRESHOLD |  | ||||||
|         redis.zrem(KEY, tag_id.to_s) |  | ||||||
|       else |  | ||||||
|         score = ((observed - expected)**2) / expected |  | ||||||
|         redis.zadd(KEY, score, tag_id.to_s) |  | ||||||
|       end |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     def increment_historical_use!(tag_id, at_time) |     def increment_historical_use!(tag_id, at_time) | ||||||
|       key = "activity:tags:#{tag_id}:#{at_time.beginning_of_day.to_i}" |       key = "activity:tags:#{tag_id}:#{at_time.beginning_of_day.to_i}" | ||||||
|       redis.incrby(key, 1) |       redis.incrby(key, 1) | ||||||
|  |  | ||||||
|  | @ -254,7 +254,6 @@ Rails.application.routes.draw do | ||||||
|       resources :mutes,      only: [:index] |       resources :mutes,      only: [:index] | ||||||
|       resources :favourites, only: [:index] |       resources :favourites, only: [:index] | ||||||
|       resources :reports,    only: [:index, :create] |       resources :reports,    only: [:index, :create] | ||||||
|       resources :trends,     only: [:index] |  | ||||||
| 
 | 
 | ||||||
|       namespace :apps do |       namespace :apps do | ||||||
|         get :verify_credentials, to: 'credentials#show' |         get :verify_credentials, to: 'credentials#show' | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue