Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding a popup window when the user clicks on the Geocoding result #22

Open
franclin opened this issue Jul 25, 2022 · 13 comments
Open

Adding a popup window when the user clicks on the Geocoding result #22

franclin opened this issue Jul 25, 2022 · 13 comments

Comments

@franclin
Copy link

Thanks for the brilliant tutorial, really great and helpful. Just wanted to ask you how I can add a popup window when clicking on a marker returned by the Geocoding.

For instance, when you search for the word Mumbai at 15.41 on YouTube, I would like to add a popup info when the user clicks on the blue icon. Any thought how I can achieve that pls?

@codelikepro22
Copy link
Owner

Hey Franclin,
Can you put the video link here because I am confused which one. And we have already added the popup after search in the main map

@franclin
Copy link
Author

Hey Franclin, Can you put the video link here because I am confused which one. And we have already added the popup after search in the main map

Hi @codelikepro22 thanks for prompt reply. I was talking about this video: https://www.youtube.com/watch?v=JyPn_o_UJCM

What I wanted to achieve was that when I click on a result from the geocoding process and a marker is dropped at the location, I wanted to be able to click on that marker to display a popup window.

Hope this makes sense.

@codelikepro22
Copy link
Owner

You mean here to show the address of the marker in popup? Otherwise we still don't have the images and the other details of the room added yet. We still in the phase of adding the room and this is one of the steps.
If it is the first case and you just want to show the address, you can add useEffect that depends on lng and lat this useEffect will be triggered when there is change in the location and you can use reverse geocoding as we used it in the room page in this video:
https://www.youtube.com/watch?v=n0Rl-SsTVKc
And you can add state for the address to set this state when you receive the address from mapbox reverse geocoding. And you need another state to show the popup. You can set it to be true when you click on the marker and to false when you close this popup
if this is the case and you want me to write code for you here please let me know

@franclin
Copy link
Author

Yes I mean adding some basic information about the marker in the popup. So basically, here is the typical workflow:

  1. The user searches for a place name in the search box
  2. The geocoder shows a list in the dropdown
  3. The user clicks on a suggestion and the marker is displayed at that location
  4. The user clicks on the marker and then I would like to display a popup window containing a form

It is the code necessary to display that popup window that I am talking about.

@codelikepro22
Copy link
Owner

codelikepro22 commented Jul 26, 2022

One last question. Do you need it only after search so you need the same name or address you clicked on in the dropdown menu or you want it even if the user drags the marker to another place then clicks on it later. Because the first one is very easy and very quick and the other one for all the cases it will be little bit slower because you need to communicate every time with mapbox to change the longitude and latitude to real address. On the other side the first one we already have the address as a result from the search in the geocoder

@codelikepro22
Copy link
Owner

You know what. I will put here both solutions and you choose the right one for you

@franclin
Copy link
Author

I need it only after the search. Ideally, when the user clicks on the suggestion, the marker appears on the map at the location, then when I click on that marker I want a popup to appear. The content of that popup will be a form. I don't want to handle dragging markers for now (maybe in the future).

@codelikepro22
Copy link
Owner

You need to change these files in our project:
src/components/addRoom/addLocation/Geocoder.js

`import MapBoxGeocoder from '@mapbox/mapbox-gl-geocoder';
import { useControl } from 'react-map-gl';
import { useValue } from '../../../context/ContextProvider';
import '@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css';

const Geocoder = () => {
const { dispatch } = useValue();
const ctrl = new MapBoxGeocoder({
accessToken: process.env.REACT_APP_MAP_TOKEN,
marker: false,
collapsed: true,
});
useControl(() => ctrl);
ctrl.on('result', (e) => {
const coords = e.result.geometry.coordinates;
dispatch({
type: 'UPDATE_LOCATION',
payload: {
lng: coords[0],
lat: coords[1],
placeName: e.result?.place_name,
},
});
});
return null;
};

export default Geocoder;
`

src/components/addRoom/addLocation/AddLocation.js

`import { Box, Typography } from '@mui/material';
import ReactMapGL, {
GeolocateControl,
Marker,
NavigationControl,
Popup,
} from 'react-map-gl';
import { useValue } from '../../../context/ContextProvider';
import 'mapbox-gl/dist/mapbox-gl.css';
import { useEffect, useRef, useState } from 'react';
import Geocoder from './Geocoder';

const AddLocation = () => {
const {
state: {
location: { lng, lat, placeName },
},
dispatch,
} = useValue();
const mapRef = useRef();

const [showPopup, setShowPopup] = useState(false);

useEffect(() => {
if (!lng && !lat) {
fetch('https://ipapi.co/json')
.then((response) => {
return response.json();
})
.then((data) => {
mapRef.current.flyTo({
center: [data.longitude, data.latitude],
});
dispatch({
type: 'UPDATE_LOCATION',
payload: { lng: data.longitude, lat: data.latitude },
});
});
}
}, []);

return (
<Box
sx={{
height: 400,
position: 'relative',
}}
>
<ReactMapGL
ref={mapRef}
mapboxAccessToken={process.env.REACT_APP_MAP_TOKEN}
initialViewState={{
longitude: lng,
latitude: lat,
zoom: 8,
}}
mapStyle="mapbox://styles/mapbox/streets-v11"
>
<Marker
latitude={lat}
longitude={lng}
draggable
onDragEnd={(e) =>
dispatch({
type: 'UPDATE_LOCATION',
payload: { lng: e.lngLat.lng, lat: e.lngLat.lat },
})
}
onClick={() => setShowPopup(true)}
/>

<GeolocateControl
position="top-left"
trackUserLocation
onGeolocate={(e) =>
dispatch({
type: 'UPDATE_LOCATION',
payload: { lng: e.coords.longitude, lat: e.coords.latitude },
})
}
/>

{showPopup && (
<Popup
longitude={lng}
latitude={lat}
closeOnClick={false}
onClose={() => setShowPopup(false)}
>
{placeName}

)}



);
};

export default AddLocation;
`

src/context/ContextProvider.js

`import {
createContext,
useContext,
useEffect,
useReducer,
useRef,
} from 'react';
import reducer from './reducer';

const initialState = {
currentUser: null,
openLogin: false,
loading: false,
alert: { open: false, severity: 'info', message: '' },
profile: { open: false, file: null, photoURL: '' },
images: [],
details: { title: '', description: '', price: 0 },
location: { lng: 0, lat: 0, placeName: '' },
rooms: [],
priceFilter: 50,
addressFilter: null,
filteredRooms: [],
room: null,
users: [],
};

const Context = createContext(initialState);

export const useValue = () => {
return useContext(Context);
};

const ContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
const mapRef = useRef();
const containerRef = useRef();
useEffect(() => {
const currentUser = JSON.parse(localStorage.getItem('currentUser'));
if (currentUser) {
dispatch({ type: 'UPDATE_USER', payload: currentUser });
}
}, []);
return (
<Context.Provider value={{ state, dispatch, mapRef, containerRef }}>
{children}
</Context.Provider>
);
};

export default ContextProvider;
`

@codelikepro22
Copy link
Owner

The slower solution but more efficient for all cases:
You need to change only on component:

src/components/addRoom/addLocation/AddLocation.js

`import { Box, Typography } from '@mui/material';
import ReactMapGL, {
GeolocateControl,
Marker,
NavigationControl,
Popup,
} from 'react-map-gl';
import { useValue } from '../../../context/ContextProvider';
import 'mapbox-gl/dist/mapbox-gl.css';
import { useEffect, useRef, useState } from 'react';
import Geocoder from './Geocoder';

const AddLocation = () => {
const {
state: {
location: { lng, lat },
},
dispatch,
} = useValue();
const mapRef = useRef();
const [showPopup, setShowPopup] = useState(false);
const [placeName, setPlaceName] = useState('');
useEffect(() => {
if (lng || lat) {
const url = https://api.mapbox.com/geocoding/v5/mapbox.places/${lng},${lat}.json?access_token=${process.env.REACT_APP_MAP_TOKEN};
fetch(url)
.then((response) => response.json())
.then((data) => setPlaceName(data.features[0].place_name));
}
}, [lng, lat]);

useEffect(() => {
if (!lng && !lat) {
fetch('https://ipapi.co/json')
.then((response) => {
return response.json();
})
.then((data) => {
mapRef.current.flyTo({
center: [data.longitude, data.latitude],
});
dispatch({
type: 'UPDATE_LOCATION',
payload: { lng: data.longitude, lat: data.latitude },
});
});
}
}, []);

return (
<Box
sx={{
height: 400,
position: 'relative',
}}
>
<ReactMapGL
ref={mapRef}
mapboxAccessToken={process.env.REACT_APP_MAP_TOKEN}
initialViewState={{
longitude: lng,
latitude: lat,
zoom: 8,
}}
mapStyle="mapbox://styles/mapbox/streets-v11"
>
<Marker
latitude={lat}
longitude={lng}
draggable
onDragEnd={(e) =>
dispatch({
type: 'UPDATE_LOCATION',
payload: { lng: e.lngLat.lng, lat: e.lngLat.lat },
})
}
onClick={() => setShowPopup(true)}
/>

<GeolocateControl
position="top-left"
trackUserLocation
onGeolocate={(e) =>
dispatch({
type: 'UPDATE_LOCATION',
payload: { lng: e.coords.longitude, lat: e.coords.latitude },
})
}
/>

{showPopup && (
<Popup
longitude={lng}
latitude={lat}
closeOnClick={false}
onClose={() => setShowPopup(false)}
>
{placeName}

)}



);
};

export default AddLocation;
`

@codelikepro22
Copy link
Owner

I advise you also to watch all the videos of the project from the beginning to understand what is going on
Good luck my friend

@franclin
Copy link
Author

Thanks for that. I do have a few other questions please:

  1. Is it possible to achieve the same thing without using reducers?
  2. Looking at your original code, in the file ClusterMap.js , ideally what I am looking is to get the marker data from the GeocoderInput on line 124 and display that in the popup on line 126 in ClusterMap.js

So basically, the result geometry coordinates available on line 20 in sidebar/GeocoderInput.js I want to be able to access it from the ClusterMap.js file to display it in the popup. That is what I am trying to achieve and if possible without reducers as my projet is quite simple. I may look into using reducers at a latter version if needed.

Thanks for your brilliant answers!

@codelikepro22
Copy link
Owner

codelikepro22 commented Jul 26, 2022

You always can replace the reducer and global context with local state. Just create new state for location and instead of dispatch just use set state but you need here to pass setLocation as a prop to geocoder

@franclin
Copy link
Author

Thanks a lot for your advice, that works just fine! I will definitely watch the other videos, they are very inspirational, well done!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants