aboutsummaryrefslogtreecommitdiff
path: root/src/client/react/reducers/search.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/react/reducers/search.ts')
-rw-r--r--src/client/react/reducers/search.ts90
1 files changed, 90 insertions, 0 deletions
diff --git a/src/client/react/reducers/search.ts b/src/client/react/reducers/search.ts
new file mode 100644
index 0000000..658d3ca
--- /dev/null
+++ b/src/client/react/reducers/search.ts
@@ -0,0 +1,90 @@
+import * as fuzzy from 'fuzzy';
+import users, { User } from '../users';
+import { InputChangeAction, ChangeSelectedResultAction } from '../actions/search';
+
+export interface State {
+ results: string[],
+ selectedResult: string | null,
+ isExactMatch: boolean,
+};
+
+export type Action = InputChangeAction | ChangeSelectedResultAction;
+
+const DEFAULT_STATE: State = {
+ results: [
+ 's/18562',
+ ],
+ selectedResult: null,
+ isExactMatch: false,
+};
+
+function getSearchResults(allUsers: User[], query: string) {
+ if (query.trim() === '') {
+ return [];
+ }
+
+ const allResults = fuzzy.filter(query, allUsers, {
+ extract: (user: User) => user.value,
+ });
+
+ const firstResults = allResults.splice(0, 4);
+ const userIds = firstResults.map((result: { original: User }) => result.original.id);
+
+ return userIds;
+}
+
+const search = (state = DEFAULT_STATE, action: Action): State => {
+ switch (action.type) {
+ case 'SEARCH/INPUT_CHANGE': {
+ const results = getSearchResults(users.allUsers, action.typedValue);
+ let selectedResult = null;
+ let isExactMatch = false;
+
+ // Is the typed value exactly the same as the first result? Then show the
+ // appropiate icon instead of the generic search icon.
+ if ((results.length === 1) && (action.typedValue === users.byId[results[0]].value)) {
+ [selectedResult] = results;
+ isExactMatch = true;
+ }
+
+ return {
+ ...state,
+ results,
+ selectedResult,
+ isExactMatch,
+ };
+ }
+
+ case 'SEARCH/CHANGE_SELECTED_RESULT': {
+ const { results, isExactMatch } = state;
+
+ if (isExactMatch) return state;
+
+ const prevSelectedResult = state.selectedResult;
+ const prevSelectedResultIndex = results.indexOf(prevSelectedResult);
+ let nextSelectedResultIndex =
+ prevSelectedResultIndex + action.relativeChange;
+
+ if (nextSelectedResultIndex < -1) {
+ nextSelectedResultIndex = results.length - 1;
+ } else if (nextSelectedResultIndex > results.length - 1) {
+ nextSelectedResultIndex = -1;
+ }
+
+ const nextSelectedResult =
+ nextSelectedResultIndex === -1
+ ? null
+ : results[nextSelectedResultIndex];
+
+ return {
+ ...state,
+ selectedResult: nextSelectedResult,
+ };
+ }
+
+ default:
+ return state;
+ }
+};
+
+export default search;