Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Attach event listener on geolocation permission granted

My Setup:

I have got some very simple code:

const onSuccess = () => {
    console.log('success');
}

const onError = () => {
    console.log('error');
}

navigator.geolocation.getCurrentPosition(onSuccess, onError);

My actual code contains a bit more logic. But for simplicity, I have posted only the required code.

  • I am asked for the permission when I first visit the page.
  • Let us assume that, I grant the access to use my geolocation.
  • success is printed to the console.

So far so good...

Actual Problem:

  • Everytime I refresh the page, I get success logged in the console.
  • Actually I want something different. I want to call my function only once, when user grants access to use geolocation.

Now, if user blocks the geolocation and if he again allows the browser to use geolocation, at that time also, success should be logged. But on every refresh, it should not log success.

So, I believe, there should be some kind of eventlistener on allow button, but I don't see it mentioned anywhere.

My research:

I am not sure if I am going in the right direction, but...

On MDN docs here, I can see that there is something called PermissionStatus.onchange.

But it is experimental and not supported on Internet Explorer, Safari and Safari iOS.

If any one of you have used any alternatives, then I would love to check your ideas.

Actual use case

I am developing a weather application. The requirement is:

  • When user lands on the site, he should be asked for geolocation permission (if not already granted)

  • Once user grants permission, He should be taken to the page where he can see weather details of his city.

  • Current problem is that everytime, I refresh the page, it detects the location of the user and takes the user to his city details page. This is bad. This should only happen once, only when user grants access to geolocation.

My actual tech stack

  • React
  • Typescript

Update:


I am getting some suggestions to store the user's permission status to local-storage.

But if I store the permission in local-storage then I have some problems.

Let me explain in detail by taking an example.

Part till when everything works fine:

  • If user grants access to read his location.
  • Then I take him to city details page.
  • Then I save in local-storage that user has granted the permission.
  • Now if user refreshes the page, then everything seems to work fine.

When I get problems:

  • Now user goes to chrome settings and delete the location permissions for my site.
  • Then user refreshes my site, he will see a popup asking him for location permission.
  • If he then allows the permission, then I won't have any way to know that user has again allowed the permission because in my local-storage, I already have location granted stored. So, I won't be able to take him to city details page.
like image 925
Vishal Avatar asked Nov 16 '25 10:11

Vishal


1 Answers

After waiting for sometime, I did not get any solutions other than using local-storage. So, I took a mixed approach. In my approach, I use navigator.permissions on whichever browser has implemented it and for other browsers, I use localstorage.

I am using react, so my solution includes react code, but still the idea can be used for pure javascript or for any javascript framework.

I have created a custom hook for that:

useGeoLocation.ts

import { useState, useEffect } from 'react';

interface Coordinates {
  latitude: number;
  longitude: number;
};

const useGeoLocation = (localStorageKey = 'is-location-granted') => {
  const [shouldRequestLocation, setShouldRequestLocation] = useState(false);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<GeolocationPositionError>();
  const [coords, setCoords] = useState<Coordinates>();

  const onSuccess = (location: GeolocationPosition) => {
    setLoading(false);
    setCoords({
      latitude: location.coords.latitude,
      longitude: location.coords.longitude,
    });
    if (!('permissions' in navigator)) {
      localStorage.setItem(localStorageKey, JSON.stringify(true));
    }
  }

  const onError = (error: GeolocationPositionError) => {
    setLoading(false);
    setError(error);
  }

  useEffect(() => {
    if ('permissions' in navigator) {
      navigator.permissions.query({ name: 'geolocation' }).then((permissionStatus) => {
        if (permissionStatus.state !== 'granted') {
          setShouldRequestLocation(true);
        }
      });
    } else {
      const isLocationGranted = JSON.parse(localStorage.getItem(localStorageKey) || 'false');
      if (!isLocationGranted) {
        setShouldRequestLocation(true);
      }
    }
  }, []);

  useEffect(() => {
    if(!('geolocation' in navigator)) {
      const geoError = new GeolocationPositionError();
      setError({
        ...geoError,
        message: 'Geolocation not supported'
      });
    } else if (shouldRequestLocation) {
      setLoading(true);
      navigator.geolocation.getCurrentPosition(
        onSuccess,
        onError,
        { enableHighAccuracy: false, timeout: 10000, maximumAge: 18000000 },
      );
    }
  }, [shouldRequestLocation]);

  return { loading, error, coords };
};

export default useGeoLocation;

The code is preety straight forward, but if anyone finds any difficulties, please let me know and I will update my answer accordingly.

like image 134
Vishal Avatar answered Nov 19 '25 00:11

Vishal



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!