homeflowjs 0.10.19 → 0.10.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,16 +1,21 @@
1
1
  import SearchActionTypes from './search.types';
2
2
  // eslint-disable-next-line import/no-cycle
3
3
  import { buildQueryString } from '../search/property-search/property-search';
4
+ import { addSearchToLocalStorage } from '../app/user-history';
4
5
 
5
6
  export const setSearch = (payload) => ({
6
7
  type: SearchActionTypes.SET_SEARCH,
7
8
  payload,
8
9
  });
9
10
 
10
- export const setInitialSearch = (payload) => ({
11
- type: SearchActionTypes.SET_INITIAL_SEARCH,
12
- payload,
13
- });
11
+ export const setInitialSearch = (payload) => {
12
+ addSearchToLocalStorage(payload);
13
+
14
+ return {
15
+ type: SearchActionTypes.SET_INITIAL_SEARCH,
16
+ payload,
17
+ };
18
+ };
14
19
 
15
20
  export const setSuggestions = (payload) => ({
16
21
  type: SearchActionTypes.SET_SUGGESTIONS,
@@ -7,7 +7,6 @@ import initCookieConsent from '../cookie-consent/cookie-consent';
7
7
  import notify from './notify';
8
8
  import antiCSRF from './anti-csrf';
9
9
  import recaptcha from './recaptcha';
10
- import { addSearchToLocalStorage } from './user-history';
11
10
 
12
11
  import bookingCalendar from '../booking-calendar/booking-calendar';
13
12
  import { setThemePreferences, setThemeSettings, setAuthenticityToken } from '../actions/app.actions';
@@ -103,18 +102,6 @@ const hfInitialize = () => {
103
102
  store.dispatch(setArticles(Homeflow.get('articles')));
104
103
  }
105
104
 
106
- // Add search to add to userHstory in localStorage if this is a search results page
107
- if (
108
- pageRoute === 'properties#index'
109
- || pageRoute === 'postcodes#show'
110
- || pageRoute === 'locations#show'
111
- || pageRoute === 'counties#show'
112
- || pageRoute === 'countries#show'
113
- ) {
114
- const { search: { currentSearch } } = store.getState();
115
- addSearchToLocalStorage(currentSearch);
116
- }
117
-
118
105
  return null;
119
106
  };
120
107
 
@@ -33,272 +33,3 @@ export const addSearchToLocalStorage = (search) => {
33
33
  localStorage.setItem('searchHistory', JSON.stringify(searchHistory.slice(0, 10)));
34
34
  }
35
35
  };
36
-
37
- // const userHistory = () => {
38
- // // have to set timeout here to wait until entire search has been populated
39
- // // should find a better way of doing this
40
- // setTimeout(() => {
41
- // const { search: { currentSearch } } = store.getState();
42
-
43
- // debugger;
44
-
45
- // if (currentSearch) {
46
- // addSearchToLocalStorage(currentSearch);
47
- // }
48
- // }, 20000);
49
- // };
50
-
51
- // export default userHistory;
52
-
53
- // class Ctesius.Collections.UserHistory extends Backbone.Collection
54
-
55
- // model: Ctesius.Models.Search
56
-
57
- // localStorage: new Backbone.LocalStorage("UserHistory")
58
-
59
- // id: 'UserHistoryCollection-17072013'
60
-
61
- // walks: 1
62
-
63
- // inBrowserStorageEnabled: ->
64
- // true
65
-
66
- // @boot: ->
67
- // _collection = new Ctesius.Collections.UserHistory()
68
- // _collection.fetch({async:true, reset: true})
69
- // _collection.getMostRecentSearch().done (search) =>
70
- // _collection.mostRecentSearchValue = search
71
- // _collection.getOrCreatMostRecentSearchView().render()
72
- // _collection.getOrCreatNextAndPreviousPropertyView().render() if _collection.currentPageIsPropertyPage()
73
- // Homeflow.kickEvent('user_history_ready', _collection)
74
- // return _collection
75
-
76
- // toJSON: (a) ->
77
- // k = super(a)
78
- // _.select(k, (n)->
79
- // Object.keys(n).length != 0
80
- // )
81
-
82
- // mostRecentSearch:() ->
83
- // @mostRecentSearchValue
84
-
85
-
86
- // getMostRecentSearch:() ->
87
- // if @mrsR && @mrsR.state() == 'pending'
88
- // return @mrsR
89
- // else
90
- // @mrsR = new jQuery.Deferred()
91
- // if @currentPageIsPropertyPage()
92
- // search = @at(@mostRecentSearchPositionForCurrentProperty())
93
- // if search
94
- // @mrsR.resolve(search)
95
- // else
96
- // @createSearchFromProperty().done( (s) => @mrsR.resolve(s))
97
- // return @mrsR
98
-
99
- // createSearchFromProperty: ->
100
- // property = Ctesius.getProperty()
101
- // if Ctesius.getConfig('search_from_property')?
102
- // walkR = new jQuery.Deferred()
103
- // search = Ctesius.getConfig('search_from_property').call(property)
104
- // search.performAsData =>
105
- // walkR.resolve(search)
106
- // @addSearch(search, Homeflow.kickEvent('search_from_property_added'))
107
- // return walkR
108
- // else
109
- // search = property.generatedSearch()
110
- // @walkAlongSearchUntilPropertyFound(search, @currentPropertyId())
111
-
112
- // walkAlongSearchUntilPropertyFound:(search, property_id) ->
113
- // walkR = new jQuery.Deferred()
114
- // search.performAsData =>
115
- // property = _.find search.get('performed_data').properties, (p)->
116
- // ("" + p.property_id) == property_id
117
- // if property
118
- // @addSearch search, =>
119
- // @_most_recent = undefined
120
- // @getOrCreatNextAndPreviousPropertyView().render()
121
- // @getOrCreatMostRecentSearchView().render()
122
- // return walkR.resolve(search)
123
- // else if search.get('performed_data').pagination.has_next_page
124
- // current_page = search.get('page')
125
- // current_page = 1 unless current_page
126
- // search.unset('performed_data')
127
- // search.set('page', ( current_page + 1 ))
128
- // @walks = @walks + 1
129
- // if @walks < 10
130
- // return @walkAlongSearchUntilPropertyFound(search, property_id).done (s)=>
131
- // walkR.resolve(s)
132
- // return walkR
133
-
134
- // mostRecentSearchPositionForCurrentProperty:() ->
135
- // unless @_most_recent?
136
- // if @currentPageIsPropertyPage()
137
- // @_most_recent = false
138
- // property_id = @currentPropertyId()
139
- // search_locations = []
140
- // for search, i in @models
141
- // if search
142
- // data = search.get('performed_data')
143
- // if data
144
- // property = _.find search.get('performed_data').properties, (p)->
145
- // ("" + p.property_id) == property_id
146
- // if property
147
- // search_locations.push(i)
148
- // @_most_recent = _.max(search_locations)
149
- // @_most_recent
150
-
151
- // currentPageIsPropertyPage:() ->
152
- // Ctesius.getPathManager().isPath('property_show')
153
-
154
- // lastSearchContainsCurrentProperty:()->
155
- // current_property_id = @currentPropertyId()
156
- // last_search = @last()
157
- // property = _.find last_search.get('performed_data').properties, (p)->
158
- // ("" + p.property_id) == current_property_id
159
- // return property
160
-
161
-
162
- // currentPropertyId:() ->
163
- // match = window.location.pathname.match(/.*\/properties\/(\d*)\/(sales|lettings)/)
164
- // match[1] if match?
165
-
166
- // refererWasASearchPage:() ->
167
- // @urlIsASearchPage(document.referrer)
168
-
169
-
170
- // urlIsASearchPage: ->
171
- // Ctesius.getPathManager().isPath('property_show')
172
-
173
- // containsSearch:(search) ->
174
- // found = false
175
- // _.each @models, (v,i) =>
176
- // found = true if v.equalTo(search)
177
- // found
178
-
179
- // addSearch:(search, callback)->
180
- // @each (a)=>
181
- // @remove(a) if search.equalTo(a, true)
182
- // search.set('added', new Date().getTime())
183
- // search.performAsData =>
184
- // @add(search)
185
- // #if @length > 3
186
- // # @reset(@last(3))
187
- // @save(callback)
188
- // if @length > 9
189
- // @remove(@at(0))
190
-
191
-
192
- // comparator: 'added'
193
-
194
- // removeSearchById : (search_id)->
195
- // search = null
196
- // search = @get(search_id)
197
- // @remove(search)
198
- // @store({remove:search})
199
-
200
- // toLiquid : () ->
201
- // o = _.collect @models, (m)->
202
- // if m.get('channel')?
203
- // j = m.toJSON()
204
- // unless j.search_id?
205
- // j.search_id = m.cid
206
- // j.link = m.link()
207
- // j.description = m.description()
208
- // j
209
- // else
210
- // {}
211
- // _.select(o, (n)->
212
- // Object.keys(n).length > 2
213
- // )
214
-
215
- // positionOfPropertyInSearch:(property_id, search) ->
216
- // search = @mostRecentSearch() unless search?
217
- // index = null
218
- // for property, i in search.get('performed_data').properties
219
- // index = i if ("" + property.property_id) == property_id
220
- // index
221
-
222
- // getNextProperty:()->
223
- // return @_next_property if @_next_property
224
- // property = @propertyAfter(@currentPropertyId())
225
- // if property?
226
- // return @_next_property = property
227
- // else
228
- // if (@mostRecentSearch().get('performed_data').properties.length - 1) == @positionOfPropertyInSearch(@currentPropertyId()) && @mostRecentSearch().get('performed_data').pagination.has_next_page
229
- // #get next page
230
- // new_search = @mostRecentSearch().clone()
231
- // current_page = @mostRecentSearch().get('page')
232
- // current_page = 1 unless current_page
233
- // new_search.unset('performed_data')
234
- // new_search.set('page', ( current_page + 1 ))
235
- // new_search.performAsData =>
236
- // @addSearch(new_search)
237
- // @_next_property = new_search.get('performed_data').properties[0]
238
- // @_most_recent = undefined
239
- // @getOrCreatNextAndPreviousPropertyView().render()
240
- // return null
241
-
242
-
243
- // getPreviousProperty:->
244
- // return @_prev_property if @_prev_property
245
-
246
- // property = @propertyBefore(@currentPropertyId())
247
- // if property?
248
- // return @_prev_property = property
249
- // else
250
- // if @positionOfPropertyInSearch(@currentPropertyId()) == 0
251
- // #get next page
252
- // new_search = @mostRecentSearch().clone()
253
- // current_page = @mostRecentSearch().get('page')
254
- // if current_page
255
- // new_search.unset('performed_data')
256
- // new_search.set('page', ( current_page - 1 ))
257
- // new_search.performAsData =>
258
- // @addSearch(new_search)
259
- // @_prev_property = new_search.get('performed_data').properties[@getPropertySizeFromPagination(new_search)]
260
- // @_most_recent = undefined
261
- // @getOrCreatNextAndPreviousPropertyView().render() if @_prev_property
262
- // return null
263
-
264
- // getPropertySizeFromPagination: (new_search) ->
265
- // pagination_size = new_search.attributes.performed_data.pagination.to_record
266
- // if pagination_size != ""
267
- // return properties_size_from_pagination_size = pagination_size - 1
268
- // else
269
- // return property_value_from_pagination_size = 9
270
-
271
-
272
- // propertyAfter: (property_id, search) ->
273
- // search = @mostRecentSearch() unless search?
274
- // search.get('performed_data').properties[@positionOfPropertyInSearch(property_id, search)+1]
275
-
276
- // propertyBefore: (property_id) ->
277
- // search = @mostRecentSearch()
278
- // search.get('performed_data').properties[@positionOfPropertyInSearch(property_id, search)-1]
279
-
280
-
281
- // getOrCreatMostRecentSearchView: () ->
282
- // if @mostRecentSearchView?
283
- // return @mostRecentSearchView
284
- // else
285
- // return @mostRecentSearchView = new Ctesius.Views.MostRecentSearch({model: @})
286
-
287
- // getOrCreatNextAndPreviousPropertyView: () ->
288
- // if @NextAndPreviousPropertyView?
289
- // return @NextAndPreviousPropertyView
290
- // else
291
- // return @NextAndPreviousPropertyView = new Ctesius.Views.NextAndPreviousProperty({model: @})
292
-
293
-
294
- // initialize: (inits) ->
295
-
296
-
297
- // save : (callback) ->
298
- // Backbone.sync("update", @,
299
- // success : =>
300
- // callback() if callback
301
- // Homeflow.kickEvent('user_history_search_added', @)
302
-
303
- // error: ->
304
- // )
@@ -6,6 +6,7 @@ import PropTypes from 'prop-types';
6
6
  import Autosuggest from 'react-autosuggest';
7
7
  import debounce from 'lodash.debounce';
8
8
  import { setBranchesSearch } from '../../actions/branches.actions';
9
+ import { DEBOUNCE_DELAY } from '../../utils';
9
10
 
10
11
  const BranchesSearchInput = ({
11
12
  placeholder,
@@ -53,7 +54,7 @@ const BranchesSearchInput = ({
53
54
  setSuggestions(suggestions);
54
55
  });
55
56
 
56
- const debouncedGetSuggestions = useCallback(debounce(getSuggestions, 300), []);
57
+ const debouncedGetSuggestions = useCallback(debounce(getSuggestions, DEBOUNCE_DELAY), []);
57
58
 
58
59
  const renderSuggestion = (suggestion) => (
59
60
  <span className="react-autosuggest_span">{suggestion}</span>
package/hooks/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import useDefaultSort from './use-default-sort.hook';
2
2
  import useGeolocate from './use-geolocate';
3
+ import useOutsideClick from './use-outside-click';
3
4
 
4
5
  export {
5
6
  useDefaultSort,
6
7
  useGeolocate,
8
+ useOutsideClick,
7
9
  };
@@ -0,0 +1,18 @@
1
+ import { useEffect } from 'react';
2
+
3
+ const useOutsideClick = (ref, callback) => {
4
+ useEffect(() => {
5
+ const handleClick = (e) => {
6
+ if (ref.current && !ref.current.contains(e.target)) {
7
+ callback();
8
+ }
9
+ };
10
+ document.addEventListener('mousedown', handleClick);
11
+ return () => {
12
+ document.removeEventListener('mousedown', handleClick);
13
+ };
14
+ }, [ref]);
15
+ return ref;
16
+ };
17
+
18
+ export default useOutsideClick;
@@ -10,6 +10,7 @@ import IntroStep from '../intro-step/intro-step.component';
10
10
  import YourDetailsStep from '../your-details-step/your-details-step.component';
11
11
  import SimilarPropertiesStep from '../similar-properties-step/similar-properties-step.component';
12
12
  import ResultStep from '../result-step/result-step.component';
13
+ import { DEBOUNCE_DELAY } from '../../utils';
13
14
 
14
15
  import './instant-valuation.styles.scss';
15
16
 
@@ -82,7 +83,7 @@ class InstantValuation extends Component {
82
83
  this.getSimilarProperties = this.getSimilarProperties.bind(this);
83
84
  this.toggleSimilarProperty = this.toggleSimilarProperty.bind(this);
84
85
  this.getValuation = this.getValuation.bind(this);
85
- this.addressLookup = debounce(this.addressLookup, 1000).bind(this);
86
+ this.addressLookup = debounce(this.addressLookup, DEBOUNCE_DELAY).bind(this);
86
87
  this.reset = this.reset.bind(this);
87
88
  this.setMessage = this.setMessage.bind(this);
88
89
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "homeflowjs",
3
- "version": "0.10.19",
3
+ "version": "0.10.21",
4
4
  "description": "JavaScript toolkit for Homeflow themes",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,5 +1,5 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { connect } from 'react-redux';
1
+ import React, { useState } from 'react';
2
+ import { useSelector, useDispatch } from 'react-redux';
3
3
  import PropTypes from 'prop-types';
4
4
 
5
5
  import { loadNext } from '../../actions/properties.actions';
@@ -7,21 +7,18 @@ import DefaultLoader from '../../shared/loader.component';
7
7
  import { addSearchToLocalStorage } from '../../app/user-history';
8
8
 
9
9
  const LoadMoreButton = (props) => {
10
+ const dispatch = useDispatch();
11
+ const currentSearch = useSelector((state) => state.search?.currentSearch);
12
+ const pagination = useSelector((state) => state.properties?.pagination);
13
+
10
14
  const {
11
- loadNext,
12
- pagination,
13
15
  children,
14
- Loader = DefaultLoader,
15
- currentSearch,
16
+ Loader,
16
17
  ...otherProps
17
18
  } = props;
18
19
 
19
20
  const [loading, setLoading] = useState(false);
20
21
 
21
- useEffect(() => {
22
- addSearchToLocalStorage(currentSearch);
23
- }, [pagination])
24
-
25
22
  if (!pagination.has_next_page) return null;
26
23
 
27
24
  if (loading) return <Loader height="24px" />;
@@ -32,8 +29,11 @@ const LoadMoreButton = (props) => {
32
29
  onClick={(e) => {
33
30
  e.currentTarget.blur();
34
31
  setLoading(true);
35
- loadNext()
36
- .then(() => setLoading(false));
32
+ dispatch(loadNext())
33
+ .then(() => {
34
+ setLoading(false);
35
+ addSearchToLocalStorage(currentSearch);
36
+ });
37
37
  }}
38
38
  {...otherProps}
39
39
  >
@@ -43,21 +43,12 @@ const LoadMoreButton = (props) => {
43
43
  };
44
44
 
45
45
  LoadMoreButton.propTypes = {
46
- loadNext: PropTypes.func.isRequired,
47
- pagination: PropTypes.object.isRequired,
48
46
  children: PropTypes.node.isRequired,
47
+ Loader: PropTypes.elementType,
49
48
  };
50
49
 
51
- const mapStateToProps = state => ({
52
- pagination: state.properties.pagination,
53
- currentSearch: state.search.currentSearch,
54
- });
55
-
56
- const mapDispatchToProps = {
57
- loadNext,
50
+ LoadMoreButton.defaultProps = {
51
+ Loader: DefaultLoader,
58
52
  };
59
53
 
60
- export default connect(
61
- mapStateToProps,
62
- mapDispatchToProps,
63
- )(LoadMoreButton);
54
+ export default LoadMoreButton;
@@ -6,6 +6,7 @@ import Autosuggest from 'react-autosuggest';
6
6
 
7
7
  import { setSuggestions, setSearchField } from '../../actions/search.actions';
8
8
  import propertySearch from '../property-search/property-search';
9
+ import { DEBOUNCE_DELAY } from '../../utils';
9
10
 
10
11
  import { autosuggestTheme } from './location-input.styles';
11
12
 
@@ -15,7 +16,7 @@ class LocationInput extends Component {
15
16
 
16
17
  this.onLocationChange = this.onLocationChange.bind(this);
17
18
  // wait half a second after typing stops before fetching results
18
- this.debouncedLoadSuggestions = debounce(this.loadSuggestions, 500);
19
+ this.debouncedLoadSuggestions = debounce(this.loadSuggestions, DEBOUNCE_DELAY);
19
20
  }
20
21
 
21
22
  onLocationChange(event, { newValue }) {
@@ -106,6 +106,7 @@ SearchForm.propTypes = {
106
106
  'least-square-feet-first',
107
107
  'most-recently-launched-first',
108
108
  'most-recently-updated-first',
109
+ '',
109
110
  ]),
110
111
  submitCallback: PropTypes.func,
111
112
  };
@@ -1,10 +1,11 @@
1
- import React from 'react';
1
+ import React, { useRef } from 'react';
2
2
  import { connect } from 'react-redux';
3
3
  import {
4
4
  HashRouter as Router,
5
5
  Route,
6
6
  Switch,
7
7
  NavLink,
8
+ useHistory
8
9
  } from 'react-router-dom';
9
10
  import PropTypes from 'prop-types';
10
11
 
@@ -17,6 +18,7 @@ import ForgottenPassword from '../forgotten-password/forgotten-password.componen
17
18
  import SignOutButton from '../../sign-out-button/sign-out-button.component';
18
19
  import Loader from '../../../shared/loader.component';
19
20
  import { loadingStyles } from '../../../modal/modal.component';
21
+ import useOutsideClick from '../../../hooks/use-outside-click';
20
22
 
21
23
  import './user-profile.styles.scss';
22
24
 
@@ -32,9 +34,17 @@ const UserProfileModal = ({ currentUser, loading }) => {
32
34
  }
33
35
 
34
36
  const logo = Homeflow.get('agency_logo');
37
+ const history = useHistory();
38
+ const ref = useRef();
39
+
40
+ const handleClickOutside = () => {
41
+ history.push('/')
42
+ };
43
+
44
+ useOutsideClick(ref, handleClickOutside);
35
45
 
36
46
  return (
37
- <div className="user-profile hf-modal">
47
+ <div className="user-profile hf-modal" ref={ref}>
38
48
  <div className="user-profile__side">
39
49
  <img src={logo} alt="Logo" className="user-profile__agency-logo" />
40
50
 
package/utils/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  export { default as stampDutyCalculator } from './stamp-duty-calculator/stamp-duty-calculator';
2
2
 
3
- export const capitalizeFirstLetter = str => (
3
+ export const capitalizeFirstLetter = (str) => (
4
4
  str.charAt(0).toUpperCase() + str.slice(1)
5
- )
5
+ );
6
6
 
7
- export const camelToSnakeCase = str => (
8
- str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`)
9
- )
7
+ export const camelToSnakeCase = (str) => (
8
+ str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
9
+ );
10
10
 
11
11
  export const isEmpty = (obj) => {
12
12
  for (const key in obj) {
@@ -16,9 +16,7 @@ export const isEmpty = (obj) => {
16
16
  return true;
17
17
  };
18
18
 
19
- export const numberWithCommas = (num) => {
20
- return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
21
- }
19
+ export const numberWithCommas = (num) => num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
22
20
 
23
21
  // TODO: DRY these two functions up
24
22
  export const findSavedSearchIndex = (savedSearches, search) => {
@@ -64,9 +62,7 @@ export const formatMoney = (num) => {
64
62
  return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
65
63
  };
66
64
 
67
- export const uniqueKey = () => {
68
- return Math.random().toString(36).substring(7);
69
- };
65
+ export const uniqueKey = () => Math.random().toString(36).substring(7);
70
66
 
71
67
  export const objectDiff = (oldObject, newObject) => {
72
68
  const changed = {};
@@ -78,8 +74,10 @@ export const objectDiff = (oldObject, newObject) => {
78
74
  }
79
75
 
80
76
  return changed;
81
- }
77
+ };
82
78
 
83
- export const compact = (array) => array.filter(item => typeof item !== 'undefined' && item !== null);
79
+ export const compact = (array) => array.filter((item) => typeof item !== 'undefined' && item !== null);
84
80
 
85
81
  export const sanitizeText = (string) => new Option(string).innerHTML;
82
+
83
+ export const DEBOUNCE_DELAY = 200;