Add GET /api/v2/search which returns rich tag objects, adjust web UI (#7661)
This commit is contained in:
		
							parent
							
								
									90b64c0069
								
							
						
					
					
						commit
						8bb74e50be
					
				
					 7 changed files with 69 additions and 56 deletions
				
			
		
							
								
								
									
										8
									
								
								app/controllers/api/v2/search_controller.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								app/controllers/api/v2/search_controller.rb
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Api::V2::SearchController < Api::V1::SearchController
 | 
			
		||||
  def index
 | 
			
		||||
    @search = Search.new(search)
 | 
			
		||||
    render json: @search, serializer: REST::V2::SearchSerializer
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			@ -33,7 +33,7 @@ export function submitSearch() {
 | 
			
		|||
 | 
			
		||||
    dispatch(fetchSearchRequest());
 | 
			
		||||
 | 
			
		||||
    api(getState).get('/api/v1/search', {
 | 
			
		||||
    api(getState).get('/api/v2/search', {
 | 
			
		||||
      params: {
 | 
			
		||||
        q: value,
 | 
			
		||||
        resolve: true,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,6 +16,28 @@ const shortNumberFormat = number => {
 | 
			
		|||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const renderHashtag = hashtag => (
 | 
			
		||||
  <div className='trends__item' key={hashtag.get('name')}>
 | 
			
		||||
    <div className='trends__item__name'>
 | 
			
		||||
      <Link to={`/timelines/tag/${hashtag.get('name')}`}>
 | 
			
		||||
        #<span>{hashtag.get('name')}</span>
 | 
			
		||||
      </Link>
 | 
			
		||||
 | 
			
		||||
      <FormattedMessage id='trends.count_by_accounts' defaultMessage='{count} {rawCount, plural, one {person} other {people}} talking' values={{ rawCount: hashtag.getIn(['history', 0, 'accounts']), count: <strong>{shortNumberFormat(hashtag.getIn(['history', 0, 'accounts']))}</strong> }} />
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div className='trends__item__current'>
 | 
			
		||||
      {shortNumberFormat(hashtag.getIn(['history', 0, 'uses']))}
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div className='trends__item__sparkline'>
 | 
			
		||||
      <Sparklines width={50} height={28} data={hashtag.get('history').reverse().map(day => day.get('uses')).toArray()}>
 | 
			
		||||
        <SparklinesCurve style={{ fill: 'none' }} />
 | 
			
		||||
      </Sparklines>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export default class SearchResults extends ImmutablePureComponent {
 | 
			
		||||
 | 
			
		||||
  static propTypes = {
 | 
			
		||||
| 
						 | 
				
			
			@ -44,27 +66,7 @@ export default class SearchResults extends ImmutablePureComponent {
 | 
			
		|||
              <FormattedMessage id='trends.header' defaultMessage='Trending now' />
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            {trends && trends.map(hashtag => (
 | 
			
		||||
              <div className='trends__item' key={hashtag.get('name')}>
 | 
			
		||||
                <div className='trends__item__name'>
 | 
			
		||||
                  <Link to={`/timelines/tag/${hashtag.get('name')}`}>
 | 
			
		||||
                    #<span>{hashtag.get('name')}</span>
 | 
			
		||||
                  </Link>
 | 
			
		||||
 | 
			
		||||
                  <FormattedMessage id='trends.count_by_accounts' defaultMessage='{count} {rawCount, plural, one {person} other {people}} talking' values={{ rawCount: hashtag.getIn(['history', 0, 'accounts']), count: <strong>{shortNumberFormat(hashtag.getIn(['history', 0, 'accounts']))}</strong> }} />
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <div className='trends__item__current'>
 | 
			
		||||
                  {shortNumberFormat(hashtag.getIn(['history', 0, 'uses']))}
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <div className='trends__item__sparkline'>
 | 
			
		||||
                  <Sparklines width={50} height={28} data={hashtag.get('history').reverse().map(day => day.get('uses')).toArray()}>
 | 
			
		||||
                    <SparklinesCurve style={{ fill: 'none' }} />
 | 
			
		||||
                  </Sparklines>
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
            ))}
 | 
			
		||||
            {trends && trends.map(hashtag => renderHashtag(hashtag))}
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      );
 | 
			
		||||
| 
						 | 
				
			
			@ -74,7 +76,7 @@ export default class SearchResults extends ImmutablePureComponent {
 | 
			
		|||
      count   += results.get('accounts').size;
 | 
			
		||||
      accounts = (
 | 
			
		||||
        <div className='search-results__section'>
 | 
			
		||||
          <h5><FormattedMessage id='search_results.accounts' defaultMessage='People' /></h5>
 | 
			
		||||
          <h5><i className='fa fa-fw fa-users' /><FormattedMessage id='search_results.accounts' defaultMessage='People' /></h5>
 | 
			
		||||
 | 
			
		||||
          {results.get('accounts').map(accountId => <AccountContainer key={accountId} id={accountId} />)}
 | 
			
		||||
        </div>
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +87,7 @@ export default class SearchResults extends ImmutablePureComponent {
 | 
			
		|||
      count   += results.get('statuses').size;
 | 
			
		||||
      statuses = (
 | 
			
		||||
        <div className='search-results__section'>
 | 
			
		||||
          <h5><FormattedMessage id='search_results.statuses' defaultMessage='Toots' /></h5>
 | 
			
		||||
          <h5><i className='fa fa-fw fa-quote-right' /><FormattedMessage id='search_results.statuses' defaultMessage='Toots' /></h5>
 | 
			
		||||
 | 
			
		||||
          {results.get('statuses').map(statusId => <StatusContainer key={statusId} id={statusId} />)}
 | 
			
		||||
        </div>
 | 
			
		||||
| 
						 | 
				
			
			@ -96,13 +98,9 @@ export default class SearchResults extends ImmutablePureComponent {
 | 
			
		|||
      count += results.get('hashtags').size;
 | 
			
		||||
      hashtags = (
 | 
			
		||||
        <div className='search-results__section'>
 | 
			
		||||
          <h5><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></h5>
 | 
			
		||||
          <h5><i className='fa fa-fw fa-hashtag' /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></h5>
 | 
			
		||||
 | 
			
		||||
          {results.get('hashtags').map(hashtag => (
 | 
			
		||||
            <Link key={hashtag} className='search-results__hashtag' to={`/timelines/tag/${hashtag}`}>
 | 
			
		||||
              {hashtag}
 | 
			
		||||
            </Link>
 | 
			
		||||
          ))}
 | 
			
		||||
          {results.get('hashtags').map(hashtag => renderHashtag(hashtag))}
 | 
			
		||||
        </div>
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,7 @@ import {
 | 
			
		|||
  COMPOSE_REPLY,
 | 
			
		||||
  COMPOSE_DIRECT,
 | 
			
		||||
} from '../actions/compose';
 | 
			
		||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
 | 
			
		||||
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
 | 
			
		||||
 | 
			
		||||
const initialState = ImmutableMap({
 | 
			
		||||
  value: '',
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +39,7 @@ export default function search(state = initialState, action) {
 | 
			
		|||
    return state.set('results', ImmutableMap({
 | 
			
		||||
      accounts: ImmutableList(action.results.accounts.map(item => item.id)),
 | 
			
		||||
      statuses: ImmutableList(action.results.statuses.map(item => item.id)),
 | 
			
		||||
      hashtags: ImmutableList(action.results.hashtags),
 | 
			
		||||
      hashtags: fromJS(action.results.hashtags),
 | 
			
		||||
    })).set('submitted', true);
 | 
			
		||||
  default:
 | 
			
		||||
    return state;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3284,6 +3284,15 @@ a.status-card {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
.search__icon {
 | 
			
		||||
  &::-moz-focus-inner {
 | 
			
		||||
    border: 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &::-moz-focus-inner,
 | 
			
		||||
  &:focus {
 | 
			
		||||
    outline: 0 !important;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .fa {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 10px;
 | 
			
		||||
| 
						 | 
				
			
			@ -3333,7 +3342,6 @@ a.status-card {
 | 
			
		|||
.search-results__header {
 | 
			
		||||
  color: $dark-text-color;
 | 
			
		||||
  background: lighten($ui-base-color, 2%);
 | 
			
		||||
  border-bottom: 1px solid darken($ui-base-color, 4%);
 | 
			
		||||
  padding: 15px;
 | 
			
		||||
  font-weight: 500;
 | 
			
		||||
  font-size: 16px;
 | 
			
		||||
| 
						 | 
				
			
			@ -3346,33 +3354,21 @@ a.status-card {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
.search-results__section {
 | 
			
		||||
  margin-bottom: 20px;
 | 
			
		||||
  margin-bottom: 5px;
 | 
			
		||||
 | 
			
		||||
  h5 {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    background: darken($ui-base-color, 4%);
 | 
			
		||||
    border-bottom: 1px solid lighten($ui-base-color, 8%);
 | 
			
		||||
    cursor: default;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    padding: 15px;
 | 
			
		||||
    font-weight: 500;
 | 
			
		||||
    font-size: 16px;
 | 
			
		||||
    color: $dark-text-color;
 | 
			
		||||
 | 
			
		||||
    &::before {
 | 
			
		||||
      content: "";
 | 
			
		||||
      display: block;
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      left: 0;
 | 
			
		||||
      right: 0;
 | 
			
		||||
      top: 50%;
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      height: 0;
 | 
			
		||||
      border-top: 1px solid lighten($ui-base-color, 8%);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    span {
 | 
			
		||||
    .fa {
 | 
			
		||||
      display: inline-block;
 | 
			
		||||
      background: $ui-base-color;
 | 
			
		||||
      color: $darker-text-color;
 | 
			
		||||
      font-size: 14px;
 | 
			
		||||
      font-weight: 500;
 | 
			
		||||
      padding: 10px;
 | 
			
		||||
      position: relative;
 | 
			
		||||
      z-index: 1;
 | 
			
		||||
      cursor: default;
 | 
			
		||||
      margin-right: 5px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										7
									
								
								app/serializers/rest/v2/search_serializer.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								app/serializers/rest/v2/search_serializer.rb
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class REST::V2::SearchSerializer < ActiveModel::Serializer
 | 
			
		||||
  has_many :accounts, serializer: REST::AccountSerializer
 | 
			
		||||
  has_many :statuses, serializer: REST::StatusSerializer
 | 
			
		||||
  has_many :hashtags, serializer: REST::TagSerializer
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			@ -315,6 +315,10 @@ Rails.application.routes.draw do
 | 
			
		|||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    namespace :v2 do
 | 
			
		||||
      get '/search', to: 'search#index', as: :search
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    namespace :web do
 | 
			
		||||
      resource :settings, only: [:update]
 | 
			
		||||
      resource :embed, only: [:create]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue