diff --git a/.eslintrc.js b/.eslintrc.js index 6675d21b1b197e9e4126cb213de7298a4c84d1e3..bd7f13300338f40eb19e71c38fba5b842587935f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -17,6 +17,7 @@ module.exports = { 2, "always" ], + //'no-console': 'off', "react/jsx-uses-vars": ["error"], "space-infix-ops": ["error", {"int32Hint": true}] }, diff --git a/package-lock.json b/package-lock.json index 7250aa5b13090b8bf6d02e88807389bcb67349ab..23ca84073242e4ae4be3520e4feb04abab424d98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -441,6 +441,11 @@ "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", "dev": true }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, "atob": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.0.tgz", @@ -2325,6 +2330,14 @@ "integrity": "sha512-s8+wktIuDSLffCywiwSxQOMqtPxML11a/dtHE17tMn4B1MSWw/C22EKf7M2KGUBcDaVFEGT+S8N02geDXeuNKg==", "dev": true }, + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "requires": { + "delayed-stream": "1.0.0" + } + }, "commander": { "version": "2.13.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", @@ -2340,8 +2353,7 @@ "component-emitter": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" }, "compressible": { "version": "2.0.13", @@ -2442,6 +2454,11 @@ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", "dev": true }, + "cookiejar": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.1.tgz", + "integrity": "sha1-Qa1XsbVVlR7BcUEqgZQrHoIA00o=" + }, "copy-concurrently": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", @@ -2470,8 +2487,7 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "create-ecdh": { "version": "4.0.1", @@ -2849,6 +2865,11 @@ "rimraf": "2.6.2" } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -3815,6 +3836,11 @@ } } }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + }, "extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", @@ -4160,6 +4186,21 @@ "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", "dev": true }, + "form-data": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.6", + "mime-types": "2.1.18" + } + }, + "formidable": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", + "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==" + }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", @@ -5909,8 +5950,7 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { "version": "1.3.5", @@ -6345,8 +6385,7 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", @@ -7478,8 +7517,7 @@ "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "micromatch": { "version": "3.1.10", @@ -7515,20 +7553,17 @@ "mime": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", - "dev": true + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" }, "mime-db": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", - "dev": true + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" }, "mime-types": { "version": "2.1.18", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "dev": true, "requires": { "mime-db": "1.33.0" } @@ -9046,8 +9081,7 @@ "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, "progress": { "version": "2.0.0", @@ -9150,8 +9184,7 @@ "qs": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", - "dev": true + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" }, "query-string": { "version": "5.1.1", @@ -9461,7 +9494,6 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, "requires": { "core-util-is": "1.0.2", "inherits": "2.0.3", @@ -9593,6 +9625,11 @@ } } }, + "redux-debounced": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/redux-debounced/-/redux-debounced-0.5.0.tgz", + "integrity": "sha512-O2anhB0A6yQZH19uLETFtajcUQLcyiJcgC0hHSoFr5T3hWGtt0C5s6KNnb2RX51MwCh5VCl9ehZTv91F/rsZww==" + }, "redux-thunk": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.2.0.tgz", @@ -9922,8 +9959,7 @@ "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, "safe-regex": { "version": "1.1.0", @@ -10542,7 +10578,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "5.1.1" } @@ -10606,6 +10641,33 @@ "schema-utils": "0.4.5" } }, + "superagent": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.2.tgz", + "integrity": "sha512-gVH4QfYHcY3P0f/BZzavLreHW3T1v7hG9B+hpMQotGQqurOvhv87GcMCd6LWySmBuf+BDR44TQd0aISjVHLeNQ==", + "requires": { + "component-emitter": "1.2.1", + "cookiejar": "2.1.1", + "debug": "3.1.0", + "extend": "3.0.1", + "form-data": "2.3.2", + "formidable": "1.2.1", + "methods": "1.1.2", + "mime": "1.4.1", + "qs": "6.5.1", + "readable-stream": "2.3.6" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, "supports-color": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz", @@ -11180,8 +11242,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "util.promisify": { "version": "1.0.0", diff --git a/package.json b/package.json index e9db7e68580e63f3667876fe6c5ff61ee7de4cce..fca19ef1cf90f7aa30da0a3db604100be4f545e9 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,8 @@ "react-leaflet-fullscreen": "0.0.6", "react-redux": "^5.0.7", "redux": "^4.0.0", - "redux-thunk": "^2.2.0" + "redux-debounced": "^0.5.0", + "redux-thunk": "^2.2.0", + "superagent": "^3.8.2" } } diff --git a/src/actions/index.js b/src/actions/index.js index b281df3cc4bf75086c1e898fb3758e46d903ef3b..c9b8905b40082e50822232f002735245751ca3fe 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -3,7 +3,29 @@ export const updateQuery = (query) => ({ query }); +export const fetchResults = () => ({ + type: 'FETCH_RESULTS', + meta: { + debounce: { + time: 750 + } + } +}); + export const updateDatasets = (datasets) => ({ type: 'UPDATE_DATASETS', datasets }); + +export const startSpinner = () => ({ + type: 'START_SPINNER', +}); + +export const updateResults = (results) => ({ + type: 'UPDATE_RESULTS', + results +}); + +export const clearResults = () => ({ + type: 'CLEAR_RESULTS', +}); diff --git a/src/components/IntegrationAutosuggest.js b/src/components/IntegrationAutosuggest.js index 9c5853b9ff98d9acc2faad5eb5dff06b194cfe1c..1b2c88660a48f30c694454529e9a14acdbf22584 100644 --- a/src/components/IntegrationAutosuggest.js +++ b/src/components/IntegrationAutosuggest.js @@ -3,7 +3,6 @@ import PropTypes from 'prop-types'; import Autosuggest from 'react-autosuggest'; import match from 'autosuggest-highlight/match'; import parse from 'autosuggest-highlight/parse'; -import{ debounce } from 'lodash'; import TextField from 'material-ui/TextField'; import Paper from 'material-ui/Paper'; import { MenuItem } from 'material-ui/Menu'; @@ -88,19 +87,11 @@ const styles = theme => ({ const IntegrationAutosuggest = (props) => { - const loadSuggestions = (value) => ['']; - - const debouncedLoadSuggestions = debounce(loadSuggestions, 2000); - - const handleSuggestionsFetchRequested = ({ value }) => { - debouncedLoadSuggestions(value); - }; + const handleOnChange = (event, { newValue }) => props.updateQuery(newValue); const { classes } = props; - console.log("IntegrationAutosuggest", props) - - const handleOnChange = (event, { newValue }) => props.updateQuery(newValue); + console.log('IntegrationAutosuggest', props); return ( <Autosuggest @@ -112,8 +103,8 @@ const IntegrationAutosuggest = (props) => { }} renderInputComponent={renderInput} suggestions={props.search.suggestions} - onSuggestionsFetchRequested={handleSuggestionsFetchRequested} - onSuggestionsClearRequested={() => console.log("clear requested")} + onSuggestionsClearRequested={props.clearResults} + onSuggestionsFetchRequested={props.fetchResults} renderSuggestionsContainer={renderSuggestionsContainer} getSuggestionValue={getSuggestionValue} renderSuggestion={renderSuggestion} @@ -131,6 +122,8 @@ IntegrationAutosuggest.propTypes = { classes: PropTypes.object.isRequired, search: PropTypes.object.isRequired, updateQuery: PropTypes.func.isRequired, + fetchResults: PropTypes.func.isRequired, + clearResults: PropTypes.func.isRequired, }; export default withStyles(styles)(IntegrationAutosuggest); diff --git a/src/containers/FullWidthGrid.js b/src/containers/FullWidthGrid.js index 614da7a55b85d646debbce8993265852b9f281ca..8cf889db34c927f3ff00cc16bf74dfd8c3831f5a 100644 --- a/src/containers/FullWidthGrid.js +++ b/src/containers/FullWidthGrid.js @@ -7,7 +7,7 @@ import Grid from 'material-ui/Grid'; import ButtonAppBar from '../components/ButtonAppBar'; import IntegrationAutosuggest from '../components/IntegrationAutosuggest'; import LeafletMapContainer from '../components/LeafletMapContainer'; -import { updateQuery, updateDatasets } from '../actions'; +import { updateQuery, updateDatasets, fetchResults, clearResults } from '../actions'; const styles = theme => ({ root: { @@ -31,7 +31,8 @@ let FullWidthGrid = (props) => { <ButtonAppBar /> </Grid> <Grid item xs={12} sm={3}> - <IntegrationAutosuggest search={props.search} updateQuery={props.updateQuery} /> + <IntegrationAutosuggest search={props.search} updateQuery={props.updateQuery} + fetchResults={props.fetchResults} clearResults={props.clearResults} /> </Grid> <Grid item xs={12} sm={9}> <LeafletMapContainer /> @@ -48,12 +49,16 @@ const mapStateToProps = (state) => ({ const mapDispatchToProps = ({ updateQuery, updateDatasets, + fetchResults, + clearResults, }); FullWidthGrid.propTypes = { classes: PropTypes.object.isRequired, search: PropTypes.object.isRequired, updateQuery: PropTypes.func.isRequired, + fetchResults: PropTypes.func.isRequired, + clearResults: PropTypes.func.isRequired, }; FullWidthGrid = connect( @@ -61,6 +66,4 @@ FullWidthGrid = connect( mapDispatchToProps )(withStyles(styles)(FullWidthGrid)); - - export default FullWidthGrid; diff --git a/src/index.js b/src/index.js index 1fc268fb171860f3f308c7c677e28ea1ba63dceb..905d818b2f9e162e5b76c7bef8a86e6196e0e58e 100644 --- a/src/index.js +++ b/src/index.js @@ -4,10 +4,17 @@ import { createStore, applyMiddleware } from 'redux'; import { Provider } from 'react-redux'; import thunk from 'redux-thunk'; import reducer from './reducers'; +// import { logger, crashReporter } from './middleware/crashReporter'; +import hiplaApiMiddleware from './middleware/hiplaApiMiddleware'; +import createDebounce from 'redux-debounced'; import App from './components/App'; -const store = createStore(reducer, {}, applyMiddleware(thunk)); +const store = createStore( + reducer, + // applyMiddleware() tells createStore() how to handle middleware + applyMiddleware(thunk, createDebounce(), hiplaApiMiddleware()) +); render( <Provider store={store}> diff --git a/src/middleware/crashReporter.js b/src/middleware/crashReporter.js new file mode 100644 index 0000000000000000000000000000000000000000..92537815d1bdb53c03ac65f377d4117248ad8e7d --- /dev/null +++ b/src/middleware/crashReporter.js @@ -0,0 +1,16 @@ +export const logger = store => next => action => { + console.log('dispatching', action); + let result = next(action); + console.log('next state', store.getState()); + return result; +}; + +export const crashReporter = store => next => action => { + try { + return next(action); + } catch (err) { + console.error('Caught an exception!', err); + console.log('State', store.getState()); + throw err; + } +}; diff --git a/src/middleware/hiplaApiMiddleware.js b/src/middleware/hiplaApiMiddleware.js new file mode 100644 index 0000000000000000000000000000000000000000..324cbbaeaf2c1fc89703a0ce45c253e4055aa2e6 --- /dev/null +++ b/src/middleware/hiplaApiMiddleware.js @@ -0,0 +1,42 @@ +import { startSpinner, updateResults } from '../actions'; +import request from 'superagent'; + +const hiplaApiMiddleware = () => { + + const searchUrl = 'http://localhost:3000/search'; + + const getResults = (store) => { + const { query, datasets } = store.getState().search; + if (query.length < 3) { + return store.dispatch(updateResults([])); + } + + console.log(query, datasets); + return request(searchUrl) + .query({ q: query}) + .query({ dataset: datasets}) + .then(response => { + if (!response.ok) { + return Promise.reject(response); + } + return store.dispatch(updateResults(response.body)); + }); + }; + + // A Redux middleware + return store => next => action => { + switch(action.type) { + case 'FETCH_RESULTS': + console.log('FETCH_RESULTS'); + + store.dispatch(startSpinner); + next(action); + return getResults(store); + + default: + return next(action); + } + }; +}; + +export default hiplaApiMiddleware; diff --git a/src/reducers/search.js b/src/reducers/search.js index 9bf86fedf2e4ad60de31c67a7758c9d060beaeb9..a9de6704cd24f03835cdfd24922db2fb20fbe984 100644 --- a/src/reducers/search.js +++ b/src/reducers/search.js @@ -1,16 +1,20 @@ export const INITIAL_STATE = { query: '', - datasets: [], + datasets: ['warsa_karelian_places'], suggestions: [], }; const search = (state = INITIAL_STATE, action) => { - // console.log(state, action); + console.log(state, action); switch (action.type) { case 'UPDATE_QUERY': - return { ...state, query: action.query }; + return { ...state, query: action.query || '' }; case 'UPDATE_DATASETS': - return { ...state, datasets: action.datasets }; + return { ...state, datasets: action.datasets || [] }; + case 'UPDATE_RESULTS': + return { ...state, suggestions: action.results || [] }; + case 'CLEAR_RESULTS': + return { ...state, suggestions: [] }; default: return state; }