From 7787456fda5167e97210923e214eb815639ee099 Mon Sep 17 00:00:00 2001
From: Esko Ikkala <esko.ikkala@aalto.fi>
Date: Wed, 16 May 2018 16:28:38 +0300
Subject: [PATCH] Add result fetching

Co-authored-by Heino Erkki <erkki.heino@aalto.fi>
---
 src/actions/index.js                     |  6 +++++
 src/components/IntegrationAutosuggest.js |  3 +++
 src/containers/FullWidthGrid.js          | 15 ++++++++---
 src/epics/index.js                       | 34 +++++++++++++++++++++---
 src/reducers/results.js                  | 17 ++++++++++++
 src/reducers/search.js                   |  7 +++++
 6 files changed, 75 insertions(+), 7 deletions(-)
 create mode 100644 src/reducers/results.js

diff --git a/src/actions/index.js b/src/actions/index.js
index c5da0ac0..ebd7b777 100644
--- a/src/actions/index.js
+++ b/src/actions/index.js
@@ -6,6 +6,7 @@ export const FETCH_SUGGESTIONS_FAILED = 'FETCH_SUGGESTIONS_FAILED';
 export const UPDATE_SUGGESTIONS = 'UPDATE_SUGGESTIONS';
 export const CLEAR_SUGGESTIONS = 'CLEAR_SUGGESTIONS';
 export const FETCH_RESULTS = 'FETCH_RESULTS';
+export const FETCH_RESULTS_FAILED = 'FETCH_RESULTS_FAILED';
 export const UPDATE_RESULTS = 'UPDATE_RESULTS';
 export const CLEAR_RESULTS = 'CLEAR_RESULTS';
 export const CLEAR_ERROR = 'CLEAR_ERROR';
@@ -34,6 +35,11 @@ export const fetchSuggestionsFailed = (error) => ({
   error
 });
 
+export const fetchResultsFailed = (error) => ({
+  type: FETCH_RESULTS_FAILED,
+  error
+});
+
 export const updateSuggestions = ({ results }) => ({
   type: UPDATE_SUGGESTIONS,
   results
diff --git a/src/components/IntegrationAutosuggest.js b/src/components/IntegrationAutosuggest.js
index 09cc61d4..402ff33f 100644
--- a/src/components/IntegrationAutosuggest.js
+++ b/src/components/IntegrationAutosuggest.js
@@ -74,6 +74,7 @@ const styles = theme => ({
 const IntegrationAutosuggest = (props) => {
 
   const handleOnChange = (event, { newValue }) => props.updateQuery(newValue);
+  const handleOnSuggestionSelected = (event, { suggestion }) => props.fetchResults(suggestion);
 
   const { classes } = props;
 
@@ -96,6 +97,7 @@ const IntegrationAutosuggest = (props) => {
       renderSuggestionsContainer={renderSuggestionsContainer}
       getSuggestionValue={getSuggestionValue}
       renderSuggestion={renderSuggestion}
+      onSuggestionSelected={handleOnSuggestionSelected}
       inputProps={{
         classes,
         placeholder: 'Search place names',
@@ -112,6 +114,7 @@ IntegrationAutosuggest.propTypes = {
   updateQuery: PropTypes.func.isRequired,
   fetchSuggestions: PropTypes.func.isRequired,
   clearSuggestions: PropTypes.func.isRequired,
+  fetchResults: PropTypes.func.isRequired,
 };
 
 export default withStyles(styles)(IntegrationAutosuggest);
diff --git a/src/containers/FullWidthGrid.js b/src/containers/FullWidthGrid.js
index a3e42616..3f1976d1 100644
--- a/src/containers/FullWidthGrid.js
+++ b/src/containers/FullWidthGrid.js
@@ -12,7 +12,7 @@ import IconButton from 'material-ui/IconButton';
 import MenuIcon from '@material-ui/icons/Menu';
 import IntegrationAutosuggest from '../components/IntegrationAutosuggest';
 import LeafletMapContainer from '../components/LeafletMapContainer';
-import { updateQuery, updateDatasets, fetchSuggestions, clearSuggestions } from '../actions';
+import { updateQuery, updateDatasets, fetchSuggestions, clearSuggestions, fetchResults } from '../actions';
 import Message from '../components/Message';
 
 
@@ -50,14 +50,19 @@ let FullWidthGrid = (props) => {
                 <MenuIcon />
               </IconButton>
               <Grid item xs={3}>
-                <IntegrationAutosuggest search={props.search} updateQuery={props.updateQuery}
-                  fetchSuggestions={props.fetchSuggestions} clearSuggestions={props.clearSuggestions} />
+                <IntegrationAutosuggest
+                  search={props.search}
+                  updateQuery={props.updateQuery}
+                  fetchSuggestions={props.fetchSuggestions}
+                  clearSuggestions={props.clearSuggestions}
+                  fetchResults={props.fetchResults}
+                />
               </Grid>
             </Toolbar>
           </AppBar>
         </Grid>
         <Grid item xs={12}>
-          <LeafletMapContainer />
+          <LeafletMapContainer places={props.search.results} />
         </Grid>
       </Grid>
     </div>
@@ -74,6 +79,7 @@ const mapDispatchToProps = ({
   updateDatasets,
   fetchSuggestions,
   clearSuggestions,
+  fetchResults,
 });
 
 FullWidthGrid.propTypes = {
@@ -83,6 +89,7 @@ FullWidthGrid.propTypes = {
   updateQuery: PropTypes.func.isRequired,
   fetchSuggestions: PropTypes.func.isRequired,
   clearSuggestions: PropTypes.func.isRequired,
+  fetchResults: PropTypes.func.isRequired,
 };
 
 FullWidthGrid = connect(
diff --git a/src/epics/index.js b/src/epics/index.js
index c224289f..1c63e323 100644
--- a/src/epics/index.js
+++ b/src/epics/index.js
@@ -3,10 +3,17 @@ import _ from 'lodash';
 import { ajax } from 'rxjs/observable/dom/ajax';
 import { combineEpics } from 'redux-observable';
 import { Observable } from 'rxjs/Observable';
-import { updateSuggestions, FETCH_SUGGESTIONS, FETCH_SUGGESTIONS_FAILED } from '../actions';
+import {
+  updateSuggestions,
+  updateResults,
+  FETCH_SUGGESTIONS,
+  FETCH_SUGGESTIONS_FAILED,
+  FETCH_RESULTS,
+  FETCH_RESULTS_FAILED,
+} from '../actions';
 
 const getSuggestionsEpic = (action$, store) => {
-  const searchUrl = 'http://localhost:3000/search';
+  const searchUrl = 'http://localhost:3000/suggest';
 
   return action$.ofType(FETCH_SUGGESTIONS)
     .debounceTime(500)
@@ -27,6 +34,27 @@ const getSuggestionsEpic = (action$, store) => {
     });
 };
 
-const rootEpic = combineEpics(getSuggestionsEpic);
+const getResultsEpic = (action$, store) => {
+  const searchUrl = 'http://localhost:3000/search';
+
+  return action$.ofType(FETCH_RESULTS)
+    .switchMap(() => {
+      const { query, datasets } = store.getState().search;
+      if (query.length < 3) {
+        return [];
+      }
+      const dsParams = _.map(datasets, ds => `dataset=${ds}`).join('&');
+
+      const requestUrl = `${searchUrl}?q=${query}&${dsParams}`;
+      return ajax.getJSON(requestUrl)
+        .map(response => updateResults({ results: response }))
+        .catch(error => Observable.of({
+          type: FETCH_RESULTS_FAILED,
+          error: error,
+        }));
+    });
+};
+
+const rootEpic = combineEpics(getSuggestionsEpic, getResultsEpic);
 
 export default rootEpic;
diff --git a/src/reducers/results.js b/src/reducers/results.js
new file mode 100644
index 00000000..2732976c
--- /dev/null
+++ b/src/reducers/results.js
@@ -0,0 +1,17 @@
+import {
+  UPDATE_RESULTS,
+  CLEAR_RESULTS,
+} from '../actions';
+
+const results = (state = [], action) => {
+  switch (action.type) {
+    case UPDATE_RESULTS:
+      return action.results;
+    case CLEAR_RESULTS:
+      return [];
+    default:
+      return state;
+  }
+};
+
+export default results;
diff --git a/src/reducers/search.js b/src/reducers/search.js
index 3f756a1b..45d6d810 100644
--- a/src/reducers/search.js
+++ b/src/reducers/search.js
@@ -3,13 +3,17 @@ import {
   UPDATE_DATASETS,
   UPDATE_SUGGESTIONS,
   CLEAR_SUGGESTIONS,
+  UPDATE_RESULTS,
+  CLEAR_RESULTS
 } from '../actions';
 import suggestions from './suggestions';
+import results from './results';
 
 export const INITIAL_STATE = {
   query: '',
   datasets: ['warsa_karelian_places', 'warsa_municipalities'],
   suggestions: [],
+  results: []
 };
 
 const search = (state = INITIAL_STATE, action) => {
@@ -21,6 +25,9 @@ const search = (state = INITIAL_STATE, action) => {
     case CLEAR_SUGGESTIONS:
     case UPDATE_SUGGESTIONS:
       return { ...state, suggestions: suggestions(state.suggestions, action) };
+    case CLEAR_RESULTS:
+    case UPDATE_RESULTS:
+      return { ...state, results: results(state.results, action) };
     default:
       return state;
   }
-- 
GitLab