Listing location#3797
Conversation
shahthepro
left a comment
There was a problem hiding this comment.
Great PR @sparrowDom. A couple of comments inline.
| /> | ||
| ) | ||
| const FractionalListing = props => { | ||
| const isHausing = props.listing.subCategory === 'schema.housing' |
| } | ||
|
|
||
| steps.push({ | ||
| step: isHausing ? 6 : 4, |
There was a problem hiding this comment.
nit: Instead of the conditional check, you could perhaps have a variable counter that starts with 1 and replace the step values with counter++ in all steps. Would be less confusing.
There was a problem hiding this comment.
Yup agreed, your idea is much cleaner
| const locationInput = () => { | ||
| let className = 'px-3' | ||
| if (formLocationError) { | ||
| className += ' is-invalid' | ||
| } | ||
|
|
||
| return { | ||
| value: formLocationValue, | ||
| className, | ||
| name: 'location', | ||
| onChange: e => { | ||
| setFormLocationValue(e.target.value) | ||
| setFormLocationError(null) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Does this have to be a function? I see this being called once once. Calling a function each time a component renders seems like a unwanted (still negligible) overhead.
There was a problem hiding this comment.
It needs to be a function so when the formLocationError state changes, it adds the error class to the html node.
There was a problem hiding this comment.
I might be wrong here. But changing that function definition (and it's call in the next line) to something like should work right?
Or am I missing something here?
let className = 'px-3'
if (formLocationError) {
className += ' is-invalid'
}
const input = {
value: formLocationValue,
className,
name: 'location',
onChange: e => {
setFormLocationValue(e.target.value)
setFormLocationError(null)
}
}
The function gets called all the time and it only populates an object that inside input, AFAICT. Why not populate the object directly instead of a function call?
There was a problem hiding this comment.
Ah yes, I don't know what I was thinking. You are right thanks!
| const saveLocation = (latitude, longitude) => { | ||
| listing.exactLocation = { | ||
| latitude, | ||
| longitude | ||
| } | ||
| onChange({ ...listing }) | ||
| setFetchingLocation(false) | ||
| setCurrentLocationError(null) | ||
| setRedirect(true) | ||
| } |
There was a problem hiding this comment.
Can probably use useCallback for these functions.
| const fetchCurrentLocation = async e => { | ||
| e.preventDefault() | ||
| setFetchingLocation(true) | ||
| setCurrentLocationError(null) | ||
|
|
||
| if (window.webViewBridge) { | ||
| if (window.webViewBridge.send) { | ||
| const onSuccess = data => { | ||
| if (data.locationAvailable) { | ||
| saveLocation( | ||
| data.position.coords.latitude, | ||
| data.position.coords.longitude | ||
| ) | ||
| } else { | ||
| setCurrentLocationError(data.error) | ||
| } | ||
| } | ||
|
|
||
| window.webViewBridge.send('getCurrentPosition', null, onSuccess) | ||
| } | ||
| } else if (navigator.geolocation) { | ||
| navigator.geolocation.getCurrentPosition( | ||
| position => { | ||
| saveLocation(position.coords.latitude, position.coords.longitude) | ||
| }, | ||
| error => { | ||
| setCurrentLocationError(error.message) | ||
| setFetchingLocation(false) | ||
| } | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| const renderExistingLocationMap = () => { | ||
| let containerStyle = { | ||
| height: '400px' | ||
| } | ||
| if (isMobile) { | ||
| containerStyle = { | ||
| ...containerStyle, | ||
| marginLeft: '-30px', | ||
| marginRight: '-30px' | ||
| } | ||
| } | ||
|
|
||
| return ( | ||
| <LocationMap | ||
| googleMapURL={`https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=${process | ||
| .env.GOOGLE_MAPS_API_KEY || DEFAULT_GOOGLE_MAPS_API_KEY}`} | ||
| loadingElement={<div style={{ height: '100%' }} />} | ||
| containerElement={ | ||
| <div className="mt-3 mt-md-4" style={containerStyle} /> | ||
| } | ||
| mapElement={<div style={{ height: '100%' }} />} | ||
| defaultCenter={{ | ||
| latitude: listing.location.latitude, | ||
| longitude: listing.location.longitude | ||
| }} | ||
| circleOptions={{ | ||
| latitude: listing.location.latitude, | ||
| longitude: listing.location.longitude, | ||
| radius: listing.location.accuracyInMeters | ||
| }} | ||
| readonly={true} | ||
| /> | ||
| ) | ||
| } | ||
|
|
||
| const renderExistingLocation = () => { | ||
| return ( | ||
| <> | ||
| <h1> | ||
| <Link to={prev} className="back d-md-none" /> | ||
| <fbt desc="createListing.listingLocation">Listing Location</fbt> | ||
| </h1> | ||
| <div className="step-description mb-0"> | ||
| <fbt desc="createListing.existingListingLocation"> | ||
| This is the location currently set on the listing. | ||
| </fbt> | ||
| </div> | ||
| <div className="row location-obfuscation-map"> | ||
| <div className="listing-step"> | ||
| <div className="col-md-8 px-0 px-md-3"> | ||
| <div className="form-group">{renderExistingLocationMap()}</div> | ||
| </div> | ||
| <div className="actions mt-auto"> | ||
| <button | ||
| className="btn btn-outline-primary mb-3" | ||
| onClick={() => { | ||
| delete listing.location | ||
| setForceEditLocation(true) | ||
| }} | ||
| > | ||
| <fbt desc="editListing.editLocation">Edit location</fbt> | ||
| </button> | ||
| <button | ||
| className="btn btn-primary" | ||
| onClick={() => { | ||
| listing.skipLocationObfuscationForward = true | ||
| onChange({ ...listing }) | ||
| setRedirect(true) | ||
| }} | ||
| > | ||
| <fbt desc="editListing.useCurrentLocation"> | ||
| Use current location | ||
| </fbt> | ||
| </button> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </> | ||
| ) | ||
| } | ||
|
|
||
| const renderInputLocation = () => { | ||
| return ( | ||
| <> | ||
| <h1> | ||
| <Link to={prev} className="back d-md-none" /> | ||
| <fbt desc="createListing.listingLocation">Listing Location</fbt> | ||
| </h1> | ||
| <div className="step-description mb-0 px-4 px-md-0"> | ||
| <fbt desc="createListing.whereIsListingLocated"> | ||
| Where is your listing located? | ||
| </fbt> | ||
| </div> | ||
| <div className="row location"> | ||
| <div className="col-md-8"> | ||
| <form | ||
| className="listing-step" | ||
| onSubmit={e => { | ||
| e.preventDefault() | ||
| Geocode.setApiKey( | ||
| process.env.GOOGLE_MAPS_API_KEY || DEFAULT_GOOGLE_MAPS_API_KEY | ||
| ) | ||
| Geocode.enableDebug() | ||
| listing.locationAddress = formLocationValue | ||
| Geocode.fromAddress(formLocationValue).then( | ||
| response => { | ||
| const { lat, lng } = response.results[0].geometry.location | ||
| saveLocation(lat, lng) | ||
| }, | ||
| error => { | ||
| setFormLocationError(error.message) | ||
| console.error(error.message) | ||
| } | ||
| ) | ||
| }} | ||
| > | ||
| <div className="form-group"> | ||
| <input | ||
| {...input} | ||
| placeholder={fbt( | ||
| '123 Main St.', | ||
| 'createListing.locationPlaceholder' | ||
| )} | ||
| /> | ||
| <button | ||
| className={`btn btn-outline-primary btn-location d-flex align-items-center justify-content-center`} | ||
| onClick={fetchCurrentLocation} | ||
| > | ||
| {!fetchingLocation && ( | ||
| <> | ||
| <img className="mr-2" src="images/location-icon.svg" /> | ||
| <div> | ||
| <fbt desc="createListing.useCurrentLocation"> | ||
| Use current location | ||
| </fbt> | ||
| </div> | ||
| </> | ||
| )} | ||
| {fetchingLocation && ( | ||
| <fbt desc="createListing.fetchingLocation"> | ||
| Fetching location | ||
| </fbt> | ||
| )} | ||
| </button> | ||
| {currentLocationError && ( | ||
| <div className="invalid-feedback d-flex justify-content-center mt-3"> | ||
| {currentLocationError} | ||
| </div> | ||
| )} | ||
| </div> | ||
|
|
||
| <div className="actions mt-auto"> | ||
| <button type="submit" className="btn btn-primary"> | ||
| <fbt desc="next">Next</fbt> | ||
| </button> | ||
| </div> | ||
| </form> | ||
| </div> | ||
| </div> | ||
| </> | ||
| ) | ||
| } |
There was a problem hiding this comment.
Same comment as above. Could use useCallback or may be break down into smaller components.
There was a problem hiding this comment.
Yup have used useCallback thanks!
| } | ||
|
|
||
| if (readonly && circleOptions && circleOptions.radius) { | ||
| // 30 is minRaidius. This is a reverse function of getCircleRadius in ListingObfscation |
There was a problem hiding this comment.
2 typos in 1 line -.- thanks!
| <string>We will not be using the location</string> | ||
| <key>NSLocationWhenInUseUsageDescription</key> | ||
| <string>We will not be using the location</string> | ||
| <string>We would like to determine listing's location. </string> |
There was a problem hiding this comment.
Maybe a bit more verbose since Apple has been so annoying about these:
We would like to use your location to add location data to any listings that you create
There was a problem hiding this comment.
That sounds better thanks!
tomlinton
left a comment
There was a problem hiding this comment.
Mobile looks good from my end, will test it out with the new release! Great work @sparrowDom!
Description:
Adds listing location functionality to
For Rent / Housinglisting types:current locationfeature to workImportant (!):
GOOGLE_MAPS_API_KEYChecklist:
fbtfor translationnpm run translate