From 6b54e88b6235facda7f9cf4b3b6902b505c43a7e Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 28 Oct 2021 14:23:54 -0400 Subject: [PATCH] Search: refactor search reducer, add tests --- app/soapbox/reducers/__tests__/search-test.js | 38 ++++++++ app/soapbox/reducers/search.js | 97 +++++++++++-------- 2 files changed, 97 insertions(+), 38 deletions(-) diff --git a/app/soapbox/reducers/__tests__/search-test.js b/app/soapbox/reducers/__tests__/search-test.js index 73d3186a3..6a872ab07 100644 --- a/app/soapbox/reducers/__tests__/search-test.js +++ b/app/soapbox/reducers/__tests__/search-test.js @@ -1,4 +1,8 @@ import reducer from '../search'; +import { + SEARCH_CHANGE, + SEARCH_CLEAR, +} from 'soapbox/actions/search'; import { Map as ImmutableMap } from 'immutable'; describe('search reducer', () => { @@ -12,4 +16,38 @@ describe('search reducer', () => { filter: 'accounts', })); }); + + describe('SEARCH_CHANGE', () => { + it('sets the value', () => { + const state = ImmutableMap({ value: 'hell' }); + const action = { type: SEARCH_CHANGE, value: 'hello' }; + expect(reducer(state, action).get('value')).toEqual('hello'); + }); + }); + + describe('SEARCH_CLEAR', () => { + it('resets the state', () => { + const state = ImmutableMap({ + value: 'hello world', + submitted: true, + submittedValue: 'hello world', + hidden: false, + results: ImmutableMap(), + filter: 'statuses', + }); + + const action = { type: SEARCH_CLEAR }; + + const expected = ImmutableMap({ + value: '', + submitted: false, + submittedValue: '', + hidden: false, + results: ImmutableMap(), + filter: 'accounts', + }); + + expect(reducer(state, action)).toEqual(expected); + }); + }); }); diff --git a/app/soapbox/reducers/search.js b/app/soapbox/reducers/search.js index d4c3f1a59..009fc3673 100644 --- a/app/soapbox/reducers/search.js +++ b/app/soapbox/reducers/search.js @@ -13,7 +13,7 @@ import { COMPOSE_REPLY, COMPOSE_DIRECT, } from '../actions/compose'; -import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; +import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable'; const initialState = ImmutableMap({ value: '', @@ -24,20 +24,65 @@ const initialState = ImmutableMap({ filter: 'accounts', }); +const toIds = items => { + return ImmutableOrderedSet(items.map(item => item.id)); +}; + +const getResultsFilter = results => { + if (results.accounts.length > 0) { + return 'accounts'; + } else if (results.statuses.length > 0) { + return 'statuses'; + } else if (results.hashtags.length > 0) { + return 'hashtags'; + } else { + return 'accounts'; + } +}; + +const importResults = (state, results) => { + const filter = getResultsFilter(results); + + return state.withMutations(state => { + state.set('results', ImmutableMap({ + accounts: toIds(results.accounts), + statuses: toIds(results.accounts), + hashtags: fromJS(results.hashtags), // it's a list of maps + accountsHasMore: results.accounts.length >= 20, + statusesHasMore: results.statuses.length >= 20, + hashtagsHasMore: results.hashtags.length >= 20, + accountsLoaded: true, + statusesLoaded: true, + hashtagsLoaded: true, + })); + + state.set('submitted', true); + state.set('filter', filter); + }); +}; + +const paginateResults = (state, searchType, results) => { + return state.withMutations(state => { + state.setIn(['results', `${searchType}HasMore`], results[searchType].length >= 20); + state.setIn(['results', `${searchType}Loaded`], true); + state.updateIn(['results', searchType], items => items.concat(results[searchType].map(item => item.id))); + }); +}; + +const handleSubmitted = (state, value) => { + return state.withMutations(state => { + state.set('results', ImmutableMap()); + state.set('submitted', true); + state.set('submittedValue', value); + }); +}; + export default function search(state = initialState, action) { switch(action.type) { case SEARCH_CHANGE: - return state.withMutations(map => { - map.set('value', action.value); - }); + return state.set('value', action.value); case SEARCH_CLEAR: - return state.withMutations(map => { - map.set('value', ''); - map.set('results', ImmutableMap()); - map.set('submitted', false); - map.set('hidden', false); - map.set('filter', 'accounts'); - }); + return initialState; case SEARCH_SHOW: return state.set('hidden', false); case COMPOSE_REPLY: @@ -45,39 +90,15 @@ export default function search(state = initialState, action) { case COMPOSE_DIRECT: return state.set('hidden', true); case SEARCH_FETCH_REQUEST: - return state.withMutations(map => { - map.set('results', ImmutableMap()); - map.set('submitted', true); - map.set('submittedValue', action.value); - }); + return handleSubmitted(state, action.value); case SEARCH_FETCH_SUCCESS: - return state.set('results', ImmutableMap({ - accounts: ImmutableList(action.results.accounts.map(item => item.id)), - statuses: ImmutableList(action.results.statuses.map(item => item.id)), - hashtags: fromJS(action.results.hashtags), - accountsHasMore: action.results.accounts.length >= 20, - statusesHasMore: action.results.statuses.length >= 20, - hashtagsHasMore: action.results.hashtags.length >= 20, - accountsLoaded: true, - statusesLoaded: true, - hashtagsLoaded: true, - })).set('submitted', true).set('filter', action.results.accounts.length > 0 - ? 'accounts' - : action.results.statuses.length > 0 - ? 'statuses' - : action.results.hashtags.length > 0 - ? 'hashtags' - : 'accounts'); + return importResults(state, action.results); case SEARCH_FILTER_SET: return state.set('filter', action.value); case SEARCH_EXPAND_REQUEST: return state.setIn(['results', `${action.searchType}Loaded`], false); case SEARCH_EXPAND_SUCCESS: - return state.withMutations((state) => { - state.setIn(['results', `${action.searchType}HasMore`], action.results[action.searchType].length >= 20); - state.setIn(['results', `${action.searchType}Loaded`], true); - state.updateIn(['results', action.searchType], list => list.concat(action.results[action.searchType].map(item => item.id))); - }); + return paginateResults(state, action.searchType, action.results); default: return state; }