mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-26 06:47:17 +10:00
Merge pull request #358 from sharetribe/map-review-changes
Map review changes
This commit is contained in:
commit
77eb4b4031
8 changed files with 78 additions and 41 deletions
|
|
@ -54,7 +54,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<div id="root"><!--!body--></div>
|
||||
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAIhL8ykNE8WcPihRdpkL33TXng-0SYDr0&libraries=places"></script>
|
||||
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDZhsJGsRv0c9HykUHfnVAIVOb1tWtqrTg&libraries=places"></script>
|
||||
<!--
|
||||
Stripe script should be on every page, not just the pages that
|
||||
use the API:
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ const MapWithGoogleMap = withGoogleMap(props => {
|
|||
isOpenOnModal,
|
||||
listings,
|
||||
listingOpen,
|
||||
onBoundsChanged,
|
||||
onIdle,
|
||||
onCloseAsModal,
|
||||
onListingClicked,
|
||||
onMapLoad,
|
||||
|
|
@ -76,7 +76,11 @@ const MapWithGoogleMap = withGoogleMap(props => {
|
|||
});
|
||||
|
||||
const openedCard = listingOpen
|
||||
? <SearchMapListingCard key={listingOpen.id.uuid} listing={listingOpen} onClickCallback={onCloseAsModal} />
|
||||
? <SearchMapListingCard
|
||||
key={listingOpen.id.uuid}
|
||||
listing={listingOpen}
|
||||
onClickCallback={onCloseAsModal}
|
||||
/>
|
||||
: null;
|
||||
|
||||
return (
|
||||
|
|
@ -93,7 +97,7 @@ const MapWithGoogleMap = withGoogleMap(props => {
|
|||
fullscreenControl: !isOpenOnModal,
|
||||
}}
|
||||
ref={onMapLoad}
|
||||
onBoundsChanged={onBoundsChanged}
|
||||
onIdle={onIdle}
|
||||
>
|
||||
{priceLabels}
|
||||
{openedCard}
|
||||
|
|
@ -127,7 +131,7 @@ export class SearchMapComponent extends Component {
|
|||
// Do not call fitMapToBounds if bounds are the same.
|
||||
// Our bounds are viewport bounds, and fitBounds will try to add margins around those bounds
|
||||
// that would result to zoom-loop (bound change -> fitmap -> bounds change -> ...)
|
||||
if (!isEqual(nextProps.bounds, currentBounds)) {
|
||||
if (!isEqual(nextProps.bounds, currentBounds) && nextProps.useLocationSearchBounds) {
|
||||
fitMapToBounds(this.googleMap, nextProps.bounds);
|
||||
}
|
||||
}
|
||||
|
|
@ -166,7 +170,7 @@ export class SearchMapComponent extends Component {
|
|||
center,
|
||||
isOpenOnModal,
|
||||
listings,
|
||||
onBoundsChanged,
|
||||
onIdle,
|
||||
onCloseAsModal,
|
||||
zoom,
|
||||
} = this.props;
|
||||
|
|
@ -185,8 +189,8 @@ export class SearchMapComponent extends Component {
|
|||
listingOpen={this.state.listingOpen}
|
||||
onListingClicked={this.onListingClicked}
|
||||
onMapLoad={this.onMapLoadHandler}
|
||||
onBoundsChanged={() => {
|
||||
onBoundsChanged(this.googleMap);
|
||||
onIdle={() => {
|
||||
onIdle(this.googleMap);
|
||||
}}
|
||||
onCloseAsModal={() => {
|
||||
if (onCloseAsModal) {
|
||||
|
|
@ -204,6 +208,7 @@ SearchMapComponent.defaultProps = {
|
|||
className: '',
|
||||
rootClassName: null,
|
||||
mapRootClassName: null,
|
||||
useLocationSearchBounds: true,
|
||||
bounds: null,
|
||||
center: new sdkTypes.LatLng(0, 0),
|
||||
isOpenOnModal: false,
|
||||
|
|
@ -221,7 +226,8 @@ SearchMapComponent.propTypes = {
|
|||
isOpenOnModal: bool,
|
||||
listings: arrayOf(propTypes.listing),
|
||||
mapRootClassName: string,
|
||||
onBoundsChanged: func.isRequired,
|
||||
useLocationSearchBounds: bool, // eslint-disable-line react/no-unused-prop-types
|
||||
onIdle: func.isRequired,
|
||||
onCloseAsModal: func,
|
||||
rootClassName: string,
|
||||
zoom: number,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
}
|
||||
|
||||
.listingCards {
|
||||
flex-grow: 1;
|
||||
padding: 0 24px 24px 24px;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ const SearchResultsPanel = props => {
|
|||
currencyConfig={currencyConfig}
|
||||
/>
|
||||
))}
|
||||
{props.children}
|
||||
</div>
|
||||
{paginationLinks}
|
||||
</div>
|
||||
|
|
@ -35,6 +36,7 @@ const SearchResultsPanel = props => {
|
|||
};
|
||||
|
||||
SearchResultsPanel.defaultProps = {
|
||||
children: null,
|
||||
className: null,
|
||||
listings: [],
|
||||
pagination: null,
|
||||
|
|
@ -42,9 +44,10 @@ SearchResultsPanel.defaultProps = {
|
|||
search: null,
|
||||
};
|
||||
|
||||
const { array, object, string } = PropTypes;
|
||||
const { array, node, object, string } = PropTypes;
|
||||
|
||||
SearchResultsPanel.propTypes = {
|
||||
children: node,
|
||||
className: string,
|
||||
currencyConfig: propTypes.currencyConfig.isRequired,
|
||||
listings: array,
|
||||
|
|
|
|||
|
|
@ -69,6 +69,11 @@
|
|||
flex-direction: column;
|
||||
}
|
||||
|
||||
.newSearchInProgress {
|
||||
opacity: 0.1;
|
||||
transition: opacity var(--transitionStyleButton);
|
||||
}
|
||||
|
||||
.searchListingsPanel {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
|
@ -112,6 +117,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.openMobileMapSticky {
|
||||
position: sticky;
|
||||
}
|
||||
|
||||
.mapPanel {
|
||||
|
||||
@media (--viewportMedium) {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ const listingPageReducer = (state = initialState, action = {}) => {
|
|||
...state,
|
||||
searchParams: payload.searchParams,
|
||||
searchInProgress: true,
|
||||
currentPageResultIds: [],
|
||||
searchMapListingIds: [],
|
||||
searchListingsError: null,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { connect } from 'react-redux';
|
|||
import { compose } from 'redux';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { debounce, isEqual, unionWith } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import config from '../../config';
|
||||
import { withFlattenedRoutes } from '../../util/contextHelpers';
|
||||
import { googleLatLngToSDKLatLng, googleBoundsToSDKBounds } from '../../util/googleMaps';
|
||||
|
|
@ -25,8 +26,8 @@ import css from './SearchPage.css';
|
|||
const RESULT_PAGE_SIZE = 12;
|
||||
const SHARETRIBE_API_MAX_PAGE_SIZE = 100;
|
||||
const MAX_SEARCH_RESULT_PAGES_ON_MAP = 5; // 100 * 5 = 500 listings are shown on a map.
|
||||
const DEBOUNCE_MAP_BOUNDS_CHANGE = 500; // bounds_change event is fired too often while dragging
|
||||
const MODAL_BREAKPOINT = 768; /* Search is in modal on mobile layout */
|
||||
const MODAL_BREAKPOINT = 768; // Search is in modal on mobile layout
|
||||
const SEARCH_WITH_MAP_DEBOUNCE = 300; // Little bit of debounce before search is initiated.
|
||||
|
||||
const pickSearchParamsOnly = params => {
|
||||
const { address, origin, bounds } = params || {};
|
||||
|
|
@ -47,8 +48,9 @@ export class SearchPageComponent extends Component {
|
|||
// we need to by pass 2nd search created by initial 'bounds_changes' event
|
||||
this.useLocationSearchBounds = true;
|
||||
this.modalOpenedBoundsChange = false;
|
||||
this.searchMapListingsInProgress = false;
|
||||
|
||||
this.onBoundsChanged = debounce(this.onBoundsChanged.bind(this), DEBOUNCE_MAP_BOUNDS_CHANGE);
|
||||
this.onIdle = debounce(this.onIdle.bind(this), SEARCH_WITH_MAP_DEBOUNCE);
|
||||
this.fetchMoreListingsToMap = this.fetchMoreListingsToMap.bind(this);
|
||||
}
|
||||
|
||||
|
|
@ -71,7 +73,9 @@ export class SearchPageComponent extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
onBoundsChanged(googleMap) {
|
||||
// We are using Google Maps idle event instead of bounds_changed, since it will not be fired
|
||||
// too often (in the middle of map's pan or zoom activity)
|
||||
onIdle(googleMap) {
|
||||
const { flattenedRoutes, history, location } = this.props;
|
||||
|
||||
const { address, country, boundsChanged } = parse(location.search, {
|
||||
|
|
@ -104,6 +108,7 @@ export class SearchPageComponent extends Component {
|
|||
const perPage = SHARETRIBE_API_MAX_PAGE_SIZE;
|
||||
const page = 1;
|
||||
const searchParamsForMapResults = { ...searchInURL, page, perPage };
|
||||
this.searchMapListingsInProgress = true;
|
||||
|
||||
// Search more listings for map
|
||||
onSearchMapListings(searchParamsForMapResults)
|
||||
|
|
@ -112,6 +117,8 @@ export class SearchPageComponent extends Component {
|
|||
page < MAX_SEARCH_RESULT_PAGES_ON_MAP;
|
||||
if (hasNextPage) {
|
||||
onSearchMapListings({ ...searchParamsForMapResults, page: page + 1 });
|
||||
} else {
|
||||
this.searchMapListingsInProgress = false;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
|
|
@ -204,11 +211,12 @@ export class SearchPageComponent extends Component {
|
|||
bounds={bounds}
|
||||
center={origin}
|
||||
listings={mapListings || []}
|
||||
onBoundsChanged={this.onBoundsChanged}
|
||||
onIdle={this.onIdle}
|
||||
isOpenOnModal={this.state.isSearchMapOpenOnMobile}
|
||||
onCloseAsModal={() => {
|
||||
onManageDisableScrolling('SearchPage.map', false);
|
||||
}}
|
||||
useLocationSearchBounds={this.useLocationSearchBounds}
|
||||
/>
|
||||
);
|
||||
const showSearchMapInMobile = this.state.isSearchMapOpenOnMobile ? searchMap : null;
|
||||
|
|
@ -246,26 +254,33 @@ export class SearchPageComponent extends Component {
|
|||
{listingsAreLoaded && totalItems === 0 ? noResults : null}
|
||||
{searchInProgress ? loadingResults : null}
|
||||
</div>
|
||||
<div className={css.listings}>
|
||||
<div
|
||||
className={classNames(css.listings, {
|
||||
[css.newSearchInProgress]: !listingsAreLoaded,
|
||||
})}
|
||||
>
|
||||
<SearchResultsPanel
|
||||
className={css.searchListingsPanel}
|
||||
currencyConfig={config.currencyConfig}
|
||||
listings={listingsAreLoaded ? listings : []}
|
||||
listings={listings}
|
||||
pagination={listingsAreLoaded ? pagination : null}
|
||||
search={searchParamsForPagination}
|
||||
/>
|
||||
>
|
||||
<button
|
||||
className={classNames(css.openMobileMap, {
|
||||
[css.openMobileMapSticky]: listings.length > 0,
|
||||
})}
|
||||
onClick={() => {
|
||||
this.useLocationSearchBounds = true;
|
||||
this.modalOpenedBoundsChange = true;
|
||||
this.setState({ isSearchMapOpenOnMobile: true });
|
||||
}}
|
||||
>
|
||||
<MapIcon className={css.openMobileMapIcon} />
|
||||
<FormattedMessage id="SearchPage.openMapView" />
|
||||
</button>
|
||||
</SearchResultsPanel>
|
||||
</div>
|
||||
<button
|
||||
className={css.openMobileMap}
|
||||
onClick={() => {
|
||||
this.useLocationSearchBounds = true;
|
||||
this.modalOpenedBoundsChange = true;
|
||||
this.setState({ isSearchMapOpenOnMobile: true });
|
||||
}}
|
||||
>
|
||||
<MapIcon className={css.openMobileMapIcon} />
|
||||
<FormattedMessage id="SearchPage.openMapView" />
|
||||
</button>
|
||||
</div>
|
||||
<ModalInMobile
|
||||
className={css.mapPanel}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ exports[`SearchPageComponent matches snapshot 1`] = `
|
|||
} />
|
||||
</h2>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
className="">
|
||||
<SearchResultsPanel
|
||||
className={null}
|
||||
currencyConfig={
|
||||
|
|
@ -59,16 +60,18 @@ exports[`SearchPageComponent matches snapshot 1`] = `
|
|||
}
|
||||
}
|
||||
rootClassName={null}
|
||||
search={Object {}} />
|
||||
search={Object {}}>
|
||||
<button
|
||||
className=""
|
||||
onClick={[Function]}>
|
||||
<MapIcon
|
||||
className={null} />
|
||||
<FormattedMessage
|
||||
id="SearchPage.openMapView"
|
||||
values={Object {}} />
|
||||
</button>
|
||||
</SearchResultsPanel>
|
||||
</div>
|
||||
<button
|
||||
onClick={[Function]}>
|
||||
<MapIcon
|
||||
className={null} />
|
||||
<FormattedMessage
|
||||
id="SearchPage.openMapView"
|
||||
values={Object {}} />
|
||||
</button>
|
||||
</div>
|
||||
<withViewport(ModalInMobileComponent)
|
||||
id="SearchPage.map"
|
||||
|
|
@ -89,9 +92,10 @@ exports[`SearchPageComponent matches snapshot 1`] = `
|
|||
isOpenOnModal={false}
|
||||
listings={Array []}
|
||||
mapRootClassName={null}
|
||||
onBoundsChanged={[Function]}
|
||||
onCloseAsModal={[Function]}
|
||||
onIdle={[Function]}
|
||||
rootClassName={null}
|
||||
useLocationSearchBounds={true}
|
||||
zoom={11} />
|
||||
</div>
|
||||
</withViewport(ModalInMobileComponent)>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue