aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
authorNoah Loomans <noahloomans@gmail.com>2017-12-14 12:32:07 +0100
committerNoah Loomans <noahloomans@gmail.com>2017-12-14 12:32:07 +0100
commit778dfdc728a101fca9ece3a14e590d3b8e1d43e1 (patch)
treea1be824bacb17fc5cdc2e3e155d46db770a96b28 /src/client
parent503ab5c66ab524dfe36aed84a01899cd07ed2bc5 (diff)
Use id's instead of user objects
Diffstat (limited to 'src/client')
-rw-r--r--src/client/react/components/container/Results.jsx20
-rw-r--r--src/client/react/components/container/Search.jsx10
-rw-r--r--src/client/react/components/presentational/Result.jsx16
-rw-r--r--src/client/react/reducers/search.js26
-rw-r--r--src/client/react/users.js66
5 files changed, 96 insertions, 42 deletions
diff --git a/src/client/react/components/container/Results.jsx b/src/client/react/components/container/Results.jsx
index 9be2639..911ea27 100644
--- a/src/client/react/components/container/Results.jsx
+++ b/src/client/react/components/container/Results.jsx
@@ -4,27 +4,22 @@ import { connect } from 'react-redux';
import classnames from 'classnames';
import Result from '../presentational/Result';
-const Results = (({ results, selectedResult }) => (
+const Results = (({ results, isExactMatch, selectedResult }) => (
<div
className={classnames('search__results', {
- 'search__results--has-results': results.length > 0,
+ 'search__results--has-results': !isExactMatch && results.length > 0,
})}
>
- {results.map(user => (
- <Result key={user.value} user={user} selected={user === selectedResult} />
+ {!isExactMatch && results.map(userId => (
+ <Result key={userId} userId={userId} isSelected={userId === selectedResult} />
))}
</div>
));
Results.propTypes = {
- results: PropTypes.arrayOf(PropTypes.shape({
- type: PropTypes.string,
- value: PropTypes.string,
- })).isRequired,
- selectedResult: PropTypes.shape({
- type: PropTypes.string,
- value: PropTypes.string,
- }),
+ results: PropTypes.arrayOf(PropTypes.string).isRequired,
+ isExactMatch: PropTypes.bool.isRequired,
+ selectedResult: PropTypes.string,
};
Results.defaultProps = {
@@ -33,6 +28,7 @@ Results.defaultProps = {
const mapStateToProps = state => ({
results: state.search.results,
+ isExactMatch: state.search.isExactMatch,
selectedResult: state.search.selectedResult,
});
diff --git a/src/client/react/components/container/Search.jsx b/src/client/react/components/container/Search.jsx
index 50917dd..babe0c4 100644
--- a/src/client/react/components/container/Search.jsx
+++ b/src/client/react/components/container/Search.jsx
@@ -7,14 +7,10 @@ import SearchIcon from 'react-icons/lib/md/search';
import { inputChange, changeSelectedResult } from '../../actions/search';
+import users from '../../users';
import Results from './Results';
import IconFromUserType from '../presentational/IconFromUserType';
-const userShape = {
- value: PropTypes.string.isRequired,
- type: PropTypes.string.isRequired,
-};
-
class Search extends React.Component {
constructor(props) {
super(props);
@@ -73,7 +69,7 @@ class Search extends React.Component {
<div className="search__input-wrapper">
<div className="search__icon-wrapper">
<IconFromUserType
- userType={isExactMatch ? selectedResult.type : null}
+ userType={isExactMatch ? users.byId[selectedResult].type : null}
defaultIcon={<SearchIcon />}
/>
</div>
@@ -95,7 +91,7 @@ class Search extends React.Component {
Search.propTypes = {
value: PropTypes.string.isRequired,
- selectedResult: PropTypes.shape(userShape),
+ selectedResult: PropTypes.string,
isExactMatch: PropTypes.bool.isRequired,
dispatch: PropTypes.func.isRequired,
};
diff --git a/src/client/react/components/presentational/Result.jsx b/src/client/react/components/presentational/Result.jsx
index 80f65d4..0b9e024 100644
--- a/src/client/react/components/presentational/Result.jsx
+++ b/src/client/react/components/presentational/Result.jsx
@@ -1,26 +1,24 @@
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
+import users from '../../users';
import IconFromUserType from './IconFromUserType';
-const Result = ({ user, selected }) => (
+const Result = ({ userId, isSelected }) => (
<div
className={classnames('search__result', {
- 'search__result--selected': selected,
+ 'search__result--selected': isSelected,
})}
>
- <div className="search__icon-wrapper"><IconFromUserType userType={user.type} /></div>
- <div className="search__result__text">{user.value}</div>
+ <div className="search__icon-wrapper"><IconFromUserType userType={users.byId[userId].type} /></div>
+ <div className="search__result__text">{users.byId[userId].value}</div>
</div>
);
Result.propTypes = {
- user: PropTypes.shape({
- value: PropTypes.string.isRequired,
- type: PropTypes.string.isRequired,
- }).isRequired,
- selected: PropTypes.bool.isRequired,
+ userId: PropTypes.string.isRequired,
+ isSelected: PropTypes.bool.isRequired,
};
export default Result;
diff --git a/src/client/react/reducers/search.js b/src/client/react/reducers/search.js
index 6ef0f4d..cad491b 100644
--- a/src/client/react/reducers/search.js
+++ b/src/client/react/reducers/search.js
@@ -1,43 +1,42 @@
-/* global USERS */
import fuzzy from 'fuzzy';
+import users from '../users';
const DEFAULT_STATE = {
input: '',
results: [
- { type: 's', value: '18561' },
+ 's/18562',
],
selectedResult: null,
isExactMatch: false,
};
-function getSearchResults(query) {
+function getSearchResults(allUsers, query) {
if (query.trim() === '') {
return [];
}
- const allResults = fuzzy.filter(query, USERS, {
+ const allResults = fuzzy.filter(query, allUsers, {
extract: user => user.value,
});
const firstResults = allResults.splice(0, 4);
- const users = firstResults.map(result => result.original);
+ const userIds = firstResults.map(result => result.original.id);
- return users;
+ return userIds;
}
const search = (state = DEFAULT_STATE, action) => {
switch (action.type) {
case 'SEARCH/INPUT_CHANGE': {
- let results = getSearchResults(action.typedValue);
+ 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 > 0) && (action.typedValue === results[0].value)) {
+ if ((results.length === 1) && (action.typedValue === users.byId[results[0]].value)) {
[selectedResult] = results;
isExactMatch = true;
- results = results.splice(1);
}
return {
@@ -51,6 +50,9 @@ const search = (state = DEFAULT_STATE, action) => {
case 'SEARCH/CHANGE_SELECTED_RESULT': {
const { results, isExactMatch } = state;
+
+ if (isExactMatch) return state;
+
const prevSelectedResult = state.selectedResult;
const prevSelectedResultIndex = results.indexOf(prevSelectedResult);
let nextSelectedResultIndex =
@@ -62,15 +64,11 @@ const search = (state = DEFAULT_STATE, action) => {
nextSelectedResultIndex = -1;
}
- let nextSelectedResult =
+ const nextSelectedResult =
nextSelectedResultIndex === -1
? null
: results[nextSelectedResultIndex];
- if (isExactMatch) {
- nextSelectedResult = prevSelectedResult;
- }
-
return {
...state,
selectedResult: nextSelectedResult,
diff --git a/src/client/react/users.js b/src/client/react/users.js
new file mode 100644
index 0000000..01ff093
--- /dev/null
+++ b/src/client/react/users.js
@@ -0,0 +1,66 @@
+/* global USERS */
+
+import { combineReducers, createStore } from 'redux';
+
+const getId = ({ type, value }) => `${type}/${value}`;
+
+const byId = (state = {}, action) => {
+ switch (action.type) {
+ case 'USERS/ADD_USER':
+ return {
+ ...state,
+ [action.user.id]: {
+ ...action.user,
+ },
+ };
+ default:
+ return state;
+ }
+};
+
+const allIds = (state = [], action) => {
+ switch (action.type) {
+ case 'USERS/ADD_USER':
+ return [
+ ...state,
+ action.user.id,
+ ];
+ default:
+ return state;
+ }
+};
+
+const allUsers = (state = [], action) => {
+ switch (action.type) {
+ case 'USERS/ADD_USER':
+ return [
+ ...state,
+ {
+ ...action.user,
+ },
+ ];
+ default:
+ return state;
+ }
+};
+
+const store = createStore(combineReducers({
+ byId,
+ allIds,
+ allUsers,
+}));
+
+USERS.forEach((user) => {
+ store.dispatch({
+ type: 'USERS/ADD_USER',
+ user: {
+ type: user.type,
+ value: user.value,
+ id: getId(user),
+ },
+ });
+});
+
+const users = store.getState();
+
+export default users;