Skip to content

Commit a0047b2

Browse files
output from challenge 30115867
1 parent 8c70aae commit a0047b2

File tree

7 files changed

+471
-1
lines changed

7 files changed

+471
-1
lines changed

src/actions/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import reviewOpportunityActions from './reviewOpportunity';
1313
import lookupActions from './lookup';
1414
import settingsActions from './settings';
1515
import lookerActions from './looker';
16+
import memberSearchActions from './member-search';
1617

1718
export const actions = {
1819
auth: authActions.auth,
@@ -30,6 +31,7 @@ export const actions = {
3031
lookup: lookupActions.lookup,
3132
settings: settingsActions.settings,
3233
looker: lookerActions.looker,
34+
memberSearch: memberSearchActions.memberSearch,
3335
};
3436

3537
export default undefined;

src/actions/member-search.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* @module "actions.member-search"
3+
* @desc Actions for management of members search.
4+
*/
5+
import _ from 'lodash'
6+
import { createActions } from 'redux-actions';
7+
import { getService } from '../services/member-search';
8+
9+
/**
10+
* @desc Creates an action that fetchs the members data for a search term, and
11+
* adds result to the store cumulatively.
12+
* @param {String} searchTerm the search term
13+
* @param {Number} offset the number of records to skip
14+
* @param {Number} limit the maximum number of the return results
15+
* @return {Action}
16+
*/
17+
function loadMemberSearch(searchTerm, offset = 0, limit = 10) {
18+
return getService().getUsernameMatches(searchTerm, offset, limit);
19+
}
20+
21+
/**
22+
* @static
23+
* @desc Creates an action that fetchs the members data for a search tag, and
24+
* adds result to the store.
25+
* @param {Object} tag the tag
26+
* @return {Action}
27+
*/
28+
function loadMemberSearchForTag(tag) {
29+
return getService().getTopMembers(tag);
30+
}
31+
32+
/**
33+
* @static
34+
* @desc Creates an action that check if the term is a tag name. If it is unable to check,
35+
* or invalid data returned then resets the members data and search terms data in the store
36+
* to intial values.
37+
* @param {String} searchTerm the search term
38+
* @return {Action}
39+
*/
40+
function checkIfSearchTermIsATag(searchTerm) {
41+
return getService().checkIfSearchTermIsATag(searchTerm);
42+
}
43+
44+
/**
45+
* @static
46+
* @desc Creates an action that saves the current search term.
47+
* @param {String} searchTerm the search term
48+
* @return {Action}
49+
*/
50+
function setSearchTerm(searchTerm) {
51+
return {
52+
previousSearchTerm: searchTerm
53+
}
54+
}
55+
56+
/**
57+
* @static
58+
* @desc Creates an action that saves the current search tag.
59+
* @param {Object} searchTag the search tag
60+
* @return {Action}
61+
*/
62+
function setSearchTag(searchTag) {
63+
return {
64+
searchTermTag: searchTag
65+
}
66+
}
67+
68+
export default createActions({
69+
MEMBER_SEARCH: {
70+
USERNAME_SEARCH_SUCCESS: loadMemberSearch,
71+
CHECK_IF_SEARCH_TERM_IS_A_TAG: checkIfSearchTermIsATag,
72+
TOP_MEMBER_SEARCH_SUCCESS: loadMemberSearchForTag,
73+
CLEAR_MEMBER_SEARCH: _.noop,
74+
LOAD_MORE_USERNAMES: _.noop,
75+
MEMBER_SEARCH_SUCCESS: _.noop,
76+
SET_SEARCH_TERM: setSearchTerm,
77+
SET_SEARCH_TAG: setSearchTag,
78+
RESET_SEARCH_TERM: _.noop
79+
}
80+
});

src/reducers/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import settings, { factory as settingsFactory }
2222
from './settings';
2323
import looker, { factory as lookerFactory }
2424
from './looker';
25-
25+
import memberSearch, { factory as memberSearchFactory } from './member-search';
2626

2727
export function factory(options) {
2828
return redux.resolveReducers({
@@ -41,6 +41,7 @@ export function factory(options) {
4141
mySubmissionsManagement: mySubmissionsManagementFactory(options),
4242
settings: settingsFactory(options),
4343
looker: lookerFactory(options),
44+
memberSearch: memberSearchFactory(options),
4445
});
4546
}
4647

@@ -60,4 +61,5 @@ export default ({
6061
mySubmissionsManagement,
6162
settings,
6263
looker,
64+
memberSearch,
6365
});

src/reducers/member-search.js

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
/**
2+
* @module "reducers.member-search"
3+
* @desc Reducer for {@link module:actions.member-search} actions.
4+
*
5+
* State segment managed by this reducer has the following structure:
6+
* @param {Boolean} pageLoaded `true` if loading members data for a search term is done
7+
* `false`if starting loading members data for a search term,
8+
* or loading failed
9+
* @param {Boolean} loadingMore `true` if request for loading more data is in progress;
10+
* otherwise `false`
11+
* @param {Boolean} error `true` if failed to load member data; otherwise `false`
12+
* @param {Number} totalCount the number of matched members for a search term
13+
* @param {Boolean} moreMatchesAvailable `true` if there are more matched members, for
14+
* a search term, to load; otherwise `false`
15+
* @param {Array<{}>} usernameMatches contains loaded members data for a search term
16+
* @param {Array<{}>} topMembers contains loaded members data for a search tag
17+
* @param {String} previousSearchTerm the current search term
18+
* @param {Object} searchTermTag the current search tag data if the search term is a tag name;
19+
* otherwise `null`
20+
*/
21+
import _ from 'lodash';
22+
import { redux } from 'topcoder-react-utils';
23+
import actions from '../actions/member-search';
24+
import { fireErrorMessage } from '../utils/errors';
25+
26+
/**
27+
* @private
28+
* Returns the new state with the intial members data.
29+
*/
30+
function memberSearchFailure(state) {
31+
return Object.assign({}, state, {
32+
loadingMore: false,
33+
error: true,
34+
totalCount: 0,
35+
usernameMatches: [],
36+
topMembers: [],
37+
});
38+
}
39+
40+
/**
41+
* @private
42+
* Returns the new state with the intial search terms data.
43+
*/
44+
function resetSearchTerm(state) {
45+
return Object.assign({}, state, {
46+
pageLoaded: false,
47+
previousSearchTerm: null,
48+
searchTermTag: null,
49+
});
50+
}
51+
52+
/**
53+
* @private
54+
* Returns the new state with the intial members and search terms data.
55+
*/
56+
function memberSearchFailureAndResetSearchTerm(state) {
57+
let newState = state;
58+
newState = memberSearchFailure(newState);
59+
newState = resetSearchTerm(newState);
60+
return newState;
61+
}
62+
63+
/**
64+
* Handles the actual results of loading members data for a search term cumulatively,
65+
* and clear members data on request failure.
66+
* @param {Object} state
67+
* @param {Object} action
68+
* @return {Object} New state.
69+
*/
70+
function onUsernameSearchSuccess(state, action) {
71+
const { payload } = action;
72+
if (action.error) {
73+
fireErrorMessage('Could not fetch username matches', '');
74+
return memberSearchFailure(state);
75+
}
76+
77+
return Object.assign({}, state, {
78+
loadingMore: false,
79+
totalCount: payload.totalCount,
80+
moreMatchesAvailable: state.usernameMatches.length + payload.usernameMatches.length
81+
< payload.totalCount,
82+
usernameMatches: state.usernameMatches.concat(payload.usernameMatches),
83+
});
84+
}
85+
86+
/**
87+
* Clear members data and search terms data on request failure of checking if the search term is
88+
* a tag name.
89+
* @param {Object} state
90+
* @param {Object} action
91+
* @return {Object} New state if error; otherwise the same state.
92+
*/
93+
function onCheckIfSearchTermIsATag(state, action) {
94+
if (action.error) {
95+
fireErrorMessage('Could not determine if search term is a tag', '');
96+
return memberSearchFailureAndResetSearchTerm(state);
97+
}
98+
99+
return state;
100+
}
101+
102+
/**
103+
* Handles the actual results of loading members data for a search tag, and
104+
* clear members data and search terms data on request failure.
105+
* @param {Object} state
106+
* @param {Object} action
107+
* @return {Object} New state.
108+
*/
109+
function onTopMemberSearchSuccess(state, action) {
110+
const { payload } = action;
111+
if (action.error) {
112+
fireErrorMessage('Could not fetch top members', '');
113+
return memberSearchFailureAndResetSearchTerm(state);
114+
}
115+
116+
return Object.assign({}, state, {
117+
topMembers: payload.topMembers,
118+
});
119+
}
120+
121+
/**
122+
* Clear members data to the intial state.
123+
* @param {Object} state
124+
* @param {Object} action
125+
* @return {Object} New state.
126+
*/
127+
function onClearMemberSearch(state) {
128+
return Object.assign({}, state, {
129+
pageLoaded: false,
130+
loadingMore: false,
131+
error: false,
132+
totalCount: 0,
133+
usernameMatches: [],
134+
topMembers: [],
135+
});
136+
}
137+
138+
/**
139+
* Marks the request of loading more members data for a search term as in progress
140+
* @param {Object} state
141+
* @param {Object} action
142+
* @return {Object} New state.
143+
*/
144+
function onLoadMoreUsernames(state) {
145+
return Object.assign({}, state, {
146+
loadingMore: true,
147+
});
148+
}
149+
150+
/**
151+
* Marks the loaded members data for a search term or search tag (if any) as ready.
152+
* @param {Object} state
153+
* @param {Object} action
154+
* @return {Object} New state.
155+
*/
156+
function onMemberSearchSuccess(state) {
157+
return Object.assign({}, state, {
158+
pageLoaded: true,
159+
});
160+
}
161+
162+
/**
163+
* Handles setting the current search term.
164+
* @param {Object} state
165+
* @param {Object} action
166+
* @return {Object} New state.
167+
*/
168+
function onSetSearchTerm(state, action) {
169+
const { payload } = action;
170+
return Object.assign({}, state, {
171+
error: false,
172+
previousSearchTerm: payload.previousSearchTerm,
173+
});
174+
}
175+
176+
/**
177+
* Handles setting the current search tag.
178+
* @param {Object} state
179+
* @param {Object} action
180+
* @return {Object} New state.
181+
*/
182+
function onSetSearchTag(state, action) {
183+
const { payload } = action;
184+
return Object.assign({}, state, {
185+
searchTermTag: payload.searchTermTag,
186+
});
187+
}
188+
189+
/**
190+
* Handles clearing the current search term and search tag.
191+
* @param {Object} state
192+
* @param {Object} action
193+
* @return {Object} New state.
194+
*/
195+
function onResetSearchTerm(state) {
196+
return resetSearchTerm(state);
197+
}
198+
199+
/**
200+
* Creates a new member search reducer with the specified initial state.
201+
* @param {Object} initialState Optional. Initial state.
202+
* @return {Function} member search reducer.
203+
*/
204+
function create(initialState = {}) {
205+
const a = actions.memberSearch;
206+
return redux.handleActions({
207+
[a.usernameSearchSuccess]: onUsernameSearchSuccess,
208+
[a.checkIfSearchTermIsATag]: onCheckIfSearchTermIsATag,
209+
[a.topMemberSearchSuccess]: onTopMemberSearchSuccess,
210+
[a.clearMemberSearch]: onClearMemberSearch,
211+
[a.loadMoreUsernames]: onLoadMoreUsernames,
212+
[a.memberSearchSuccess]: onMemberSearchSuccess,
213+
[a.setSearchTerm]: onSetSearchTerm,
214+
[a.setSearchTag]: onSetSearchTag,
215+
[a.resetSearchTerm]: onResetSearchTerm,
216+
}, _.defaults(initialState, {
217+
pageLoaded: false,
218+
loadingMore: false,
219+
error: false,
220+
totalCount: 0,
221+
moreMatchesAvailable: false,
222+
usernameMatches: [],
223+
topMembers: [],
224+
previousSearchTerm: null,
225+
searchTermTag: null,
226+
}));
227+
}
228+
229+
/**
230+
* Factory which creates a new reducer with its initial state tailored to the
231+
* given options object, if specified (for server-side rendering). If options
232+
* object is not specified, it creates just the default reducer. Accepted options are:
233+
* @return {Promise}
234+
* @resolves {Function(state, action): state} New reducer.
235+
*/
236+
export function factory() {
237+
return Promise.resolve(create());
238+
}
239+
240+
/**
241+
* @static
242+
* @member default
243+
* @desc Reducer with default initial state.
244+
*/
245+
export default create();

src/services/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import * as user from './user';
1515
import * as lookup from './lookup';
1616
import * as userTraits from './user-traits';
1717
import * as submissions from './submissions';
18+
import * as memberSearch from './member-search';
1819

1920
export const services = {
2021
api,
@@ -31,6 +32,7 @@ export const services = {
3132
lookup,
3233
userTraits,
3334
submissions,
35+
memberSearch,
3436
};
3537

3638
export default undefined;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy