From 1fdcc24827f48f5447a4ea7ed9f08b0363130a4d Mon Sep 17 00:00:00 2001 From: Edin Dazdarevic Date: Sun, 9 Apr 2017 19:34:08 +0200 Subject: [PATCH] Router in a good shape --- web/components/Filters.js | 3 ++ web/components/Gallery.js | 2 +- web/components/ListingDetails.js | 8 +++ web/components/Listings.js | 22 +++++++- web/components/Main.js | 58 +++++++++++++++------ web/lib/handlers.js | 19 ++++--- web/lib/router.js | 88 +++++++++++++++++++++++++++----- 7 files changed, 160 insertions(+), 40 deletions(-) diff --git a/web/components/Filters.js b/web/components/Filters.js index b275f3b..00491b6 100644 --- a/web/components/Filters.js +++ b/web/components/Filters.js @@ -43,6 +43,9 @@ export default class Filters extends React.Component { } onRoomsClick(rooms) { + this.props.dispatch({type: 'UPDATE_ROUTE', action: { + params: {rooms} + }}); this.props.dispatch({type: 'SET_ROOMS', action: {rooms}}); } diff --git a/web/components/Gallery.js b/web/components/Gallery.js index 8754b3c..4a3b0a3 100644 --- a/web/components/Gallery.js +++ b/web/components/Gallery.js @@ -56,7 +56,7 @@ export default class Gallery extends React.Component { cls += ' selected' } - return
+ return
})} ) diff --git a/web/components/ListingDetails.js b/web/components/ListingDetails.js index dfb0d49..2c57258 100644 --- a/web/components/ListingDetails.js +++ b/web/components/ListingDetails.js @@ -9,6 +9,14 @@ export default class ListingDetails extends React.Component { } onBackClick() { + this.props.dispatch({type: 'UPDATE_ROUTE', action: { + toDispatch: { + type: 'BACK_TO_RESULTS' + }, + params: { + listingId: null + } + }}); this.props.dispatch({type: 'BACK_TO_RESULTS'}); } diff --git a/web/components/Listings.js b/web/components/Listings.js index 9369622..70a04b1 100644 --- a/web/components/Listings.js +++ b/web/components/Listings.js @@ -22,8 +22,22 @@ export default class Listings extends React.Component { } onListingClick(id) { + + loadListing(id).then(l => l.text()).then(l => { - console.log('listing loaded', l); + console.log('lising clicked'); + this.props.dispatch({type: 'UPDATE_ROUTE', action: { + toDispatch: { + type: 'VIEW_LISTING_DETAILS', action: { + id, + listing: JSON.parse(l) + } + }, + params: { + listingId: id + } + }}); + this.props.dispatch({type: 'VIEW_LISTING_DETAILS', action: { id, listing: JSON.parse(l) @@ -108,6 +122,12 @@ export default class Listings extends React.Component { } onSortChange (e) { + this.props.dispatch({type: 'UPDATE_ROUTE', action: { + params: { + sort: e.target.value + } + }}); + this.props.dispatch({type: 'SORT_CHANGE', action: { sort: e.target.value }}); diff --git a/web/components/Main.js b/web/components/Main.js index a66fa14..f396426 100644 --- a/web/components/Main.js +++ b/web/components/Main.js @@ -24,15 +24,18 @@ class Main extends React.Component { } if (props.initialState) { + props.initialState.sort = props.initialState.sort || state.sort; state.filters.rooms = props.initialState.rooms; state.filters.category = props.initialState.category; state.sort = props.initialState.sort || state.sort; state.listingId = props.initialState.listingId; - state.listingDetails = true; + if (state.listingId) { + state.listingDetails = true; + } } this.state = state; - this.router = new Router(this); + this.router = new Router(this, props.initialState); } dispatch ({type, action = {}}) { @@ -81,7 +84,6 @@ class Main extends React.Component { map.setCenter(place.geometry.location); map.setZoom(18); } - console.log(map.getBounds()); this.dispatch({type: 'SEARCH_PLACE_CHANGED'}); }); @@ -97,13 +99,15 @@ class Main extends React.Component { this.map = map; map.addListener('zoom_changed', () => { - console.log('zoom_changed'); this.removeAllMarkers(); this.markers = []; }); map.addListener('idle', () => { - console.log('idle'); + this.dispatch({type: 'UPDATE_ROUTE', action: {params: { + bounds: map.getBounds().toUrlValue() + }}}); + this.dispatch({type: 'MAP_IDLE'}); }); @@ -113,9 +117,7 @@ class Main extends React.Component { if (this.state.listingId) { - console.log("IT ISSSSS"); loadListing(this.state.listingId).then(l => l.text()).then(l => { - console.log('listing loaded', l); this.dispatch({type: 'VIEW_LISTING_DETAILS', action: { id: this.state.listingId, listing: JSON.parse(l) @@ -126,7 +128,6 @@ class Main extends React.Component { removeAllMarkers () { if (this.markers) { - console.log('removeAllMarkers'); this.markers.forEach((m) => m.marker.setMap(null)); } } @@ -195,7 +196,6 @@ class Main extends React.Component { }) .then(({body, totalCount}) => { body.then(p => { - console.log('results_received: ', totalCount); const data = JSON.parse(p); const listingExists = (id) => { @@ -213,7 +213,6 @@ class Main extends React.Component { newMarkers.push(m); } }); - console.log('markers_removed'); } for(const [index, prop] of data.entries()) { @@ -250,19 +249,36 @@ class Main extends React.Component { }); marker.addListener('click', () => { - console.log('clicking...', prop._id); + // Maybe move out and call when popping state if (this.state.listingId) { const prevSelected = this.findMarker(this.state.listingId); if (prevSelected) { - console.log('prevselected', prevSelected); prevSelected.marker.setIcon(this.visitedMarkerIcon()); } } marker.setIcon(this.selectedMarkerIcon()); + loadListing(prop._id).then(l => l.text()).then(l => { - console.log('listing loaded', l); + + this.dispatch({type: 'UPDATE_ROUTE', action: { + toDispatch: { + type: 'VIEW_LISTING_DETAILS', action: { + id: prop._id, + listing: JSON.parse(l) + } + }, + params: { + listingId: prop._id + } + }}); + + //this.dispatch({type: 'UPDATE_ROUTE', action: {type: 'VIEW_LISTING_DETAILS', action: { + //id: prop._id, + //listing: JSON.parse(l) + //}}}); + this.dispatch({type: 'VIEW_LISTING_DETAILS', action: { id: prop._id, listing: JSON.parse(l) @@ -293,7 +309,18 @@ class Main extends React.Component { * Refreshes search */ refreshListings(more = false) { - console.log('refreshListings'); + + // TODO: move somewhere else + + if (!more && this.state.listingId) { + + loadListing(this.state.listingId).then(l => l.text()).then(l => { + this.dispatch({type: 'VIEW_LISTING_DETAILS', action: { + id: this.state.listingId, + listing: JSON.parse(l) + }}); + }); + } if (!more) { this.loadPins(); @@ -332,7 +359,6 @@ class Main extends React.Component { }) .then(({body, totalCount}) => { body.then(p => { - console.log('results_received: ', totalCount); const data = JSON.parse(p); this.dispatch({ @@ -437,9 +463,7 @@ class Main extends React.Component { const children = []; if (this.state.listingDetails) { - console.log('CURRENT LISTINGS', this.state.listings); const listing = this.state.listing; //this.state.listings.get(this.state.listingId); - console.log(this.state); children.push( { const scrollElem = document.querySelector('.right-content'); component.savedScrollTop = scrollElem.scrollTop; + //component.router.listingId = action.id; + component.setState({ listingDetails: true, listingId: action.id, @@ -59,8 +61,7 @@ const viewListingDetails = ({ type, action }, component) => { imageIndex: 0, listing: action.listing }, () => { - //window.history.pushState({}, '', `/listing/${action.id}`); - component.router.update(); + //component.router.update(); markSeen(action.id); const m = component.findMarker(action.id); if (m) { @@ -72,7 +73,6 @@ const viewListingDetails = ({ type, action }, component) => { }; const listingsLoaded = ({ type, action }, component) => { - console.log('listings_loaded', action.more); const currentListings = new Map(); for (const listing of action.listings) { @@ -211,9 +211,10 @@ const onListingMouseOver = ({type, action}, component) => { const backToResults = ({type, action}, component) => { const prevSelected = component.findMarker(component.state.listingId); component.setState({ - listingId: undefined, + listingId: null, listingDetails: false }, () => { + //component.router.update(); if (prevSelected) { prevSelected.marker.setIcon(component.visitedMarkerIcon()); @@ -225,8 +226,6 @@ const backToResults = ({type, action}, component) => { } const loadMoreListings = ({type, action}, component) => { - console.log('loading more'); - const currentPage = component.state.page; if (currentPage * 20 < component.state.totalCount) { component.setState({ @@ -253,10 +252,15 @@ const sortChange = ({type, action}, component) => { sort: action.sort, page: 0 }, () => { + //component.router.update(); component.refreshListings(); }); } +const updateRoute = ({type, action}, component) => { + component.router.update(action); +} + const handlers = { SET_MIN_PRICE: setMinPrice, SET_MAX_PRICE: setMaxPrice, @@ -277,7 +281,8 @@ const handlers = { LOAD_MORE_LISTINGS: loadMoreListings, MAP_IDLE: mapIdle, PINS_LOADED: pinsLoaded, - SORT_CHANGE: sortChange + SORT_CHANGE: sortChange, + UPDATE_ROUTE: updateRoute }; export const handleMessage = ({ type, action }, component) => { diff --git a/web/lib/router.js b/web/lib/router.js index 5e4a206..73035b9 100644 --- a/web/lib/router.js +++ b/web/lib/router.js @@ -1,18 +1,78 @@ -export default class Router { - constructor(comp) { - this.component = comp; +class ToggleHash { + constructor(data, key) { + this.data = data; + this.key = key; + this.data[key] = {}; } - update () { - // Take the state from the component and update the URL - const {listingId, sort, minPrice, maxPrice, minSize, maxSize, filters: {rooms, category}} = this.component.state; - const bounds = this.component.map.getBounds().toUrlValue(); - const params = []; - params.push(`listingId=${listingId}`); - params.push(`sort=${sort}`); - params.push(`bounds=${bounds}`); - params.push(`rooms=${Object.keys(rooms).filter(v => rooms[v]).join(",")}`); - params.push(`category=${Object.keys(category).filter(v => category[v]).join(",")}`); - window.history.pushState({}, '', `/?${params.join("&")}`); + toggle (field) { + if (!field) { + return; + } + + const val = this.data[this.key][field]; + this.data[this.key][field] = !val; + } +} + +export default class Router { + constructor(comp, initialState) { + this.component = comp; + this.state = initialState || {}; + + this.roomsToggler = new ToggleHash(this.state, 'rooms'); + this.categoryToggler = new ToggleHash(this.state, 'category'); + + window.onpopstate = (event) => { + const state = event.state; + console.log('POPING STATE', state); + if (state) { + if (state.toDispatch) { + this.component.dispatch(state.toDispatch); + } + } + } + } + + update (state) { + const params = []; + if (state.params) { + + this.roomsToggler.toggle(state.params.rooms); + this.categoryToggler.toggle(state.params.category); + + delete state.params['rooms']; + delete state.params['category']; + + this.state = Object.assign(this.state, state.params); + + console.log('router state is: ', this.state); + const {listingId, bounds, sort, rooms = {}, category = {}} = this.state; + + if (listingId) { + params.push(`listingId=${listingId}`); + } + + params.push(`sort=${sort}`); + params.push(`bounds=${bounds}`); + params.push(`rooms=${Object.keys(rooms).filter(v => rooms[v]).join(",")}`); + params.push(`category=${Object.keys(category).filter(v => category[v]).join(",")}`); + } + + if (state.toDispatch) { + console.log('PUSHING STATE', state); + window.history.pushState(state, '', `/?${params.join("&")}`); + } else { + const oldState = window.history.state; + if (oldState) { + const newState = Object.assign(oldState, state); + console.log('REPLACING STATE', newState); + window.history.replaceState(newState, '',`/?${params.join("&")}`); + } else { + + console.log('PUSHING STATE', state); + window.history.replaceState(state, '',`/?${params.join("&")}`); + } + } } }