126 lines
4.4 KiB
JavaScript
126 lines
4.4 KiB
JavaScript
import { PermissionStatus } from 'unimodules-permissions-interface';
|
|
import { LocationAccuracy, } from './Location.types';
|
|
import { LocationEventEmitter } from './LocationEventEmitter';
|
|
class GeocoderError extends Error {
|
|
constructor() {
|
|
super('Geocoder service is not available for this device.');
|
|
this.code = 'E_NO_GEOCODER';
|
|
}
|
|
}
|
|
/**
|
|
* Converts `GeolocationPosition` to JavaScript object.
|
|
*/
|
|
function geolocationPositionToJSON(position) {
|
|
const { coords, timestamp } = position;
|
|
return {
|
|
coords: {
|
|
latitude: coords.latitude,
|
|
longitude: coords.longitude,
|
|
altitude: coords.altitude,
|
|
accuracy: coords.accuracy,
|
|
altitudeAccuracy: coords.altitudeAccuracy,
|
|
heading: coords.heading,
|
|
speed: coords.speed,
|
|
},
|
|
timestamp,
|
|
};
|
|
}
|
|
/**
|
|
* Checks whether given location didn't exceed given `maxAge` and fits in the required accuracy.
|
|
*/
|
|
function isLocationValid(location, options) {
|
|
const maxAge = typeof options.maxAge === 'number' ? options.maxAge : Infinity;
|
|
const requiredAccuracy = typeof options.requiredAccuracy === 'number' ? options.requiredAccuracy : Infinity;
|
|
const locationAccuracy = location.coords.accuracy ?? Infinity;
|
|
return Date.now() - location.timestamp <= maxAge && locationAccuracy <= requiredAccuracy;
|
|
}
|
|
/**
|
|
* Gets the permission details. The implementation is not very good as it actually requests
|
|
* for the current location, but there is no better way on web so far :(
|
|
*/
|
|
async function getPermissionsAsync() {
|
|
return new Promise(resolve => {
|
|
const resolveWithStatus = status => resolve({
|
|
status,
|
|
granted: status === PermissionStatus.GRANTED,
|
|
canAskAgain: true,
|
|
expires: 0,
|
|
});
|
|
navigator.geolocation.getCurrentPosition(() => resolveWithStatus(PermissionStatus.GRANTED), ({ code }) => {
|
|
if (code === 1 /* PERMISSION_DENIED */) {
|
|
resolveWithStatus(PermissionStatus.DENIED);
|
|
}
|
|
else {
|
|
resolveWithStatus(PermissionStatus.UNDETERMINED);
|
|
}
|
|
}, { enableHighAccuracy: false, maximumAge: Infinity });
|
|
});
|
|
}
|
|
let lastKnownPosition = null;
|
|
export default {
|
|
get name() {
|
|
return 'ExpoLocation';
|
|
},
|
|
async getProviderStatusAsync() {
|
|
return {
|
|
locationServicesEnabled: 'geolocation' in navigator,
|
|
};
|
|
},
|
|
async getLastKnownPositionAsync(options = {}) {
|
|
if (lastKnownPosition && isLocationValid(lastKnownPosition, options)) {
|
|
return lastKnownPosition;
|
|
}
|
|
return null;
|
|
},
|
|
async getCurrentPositionAsync(options) {
|
|
return new Promise((resolve, reject) => {
|
|
const resolver = position => {
|
|
lastKnownPosition = geolocationPositionToJSON(position);
|
|
resolve(lastKnownPosition);
|
|
};
|
|
navigator.geolocation.getCurrentPosition(resolver, reject, {
|
|
maximumAge: Infinity,
|
|
enableHighAccuracy: (options.accuracy ?? 0) > LocationAccuracy.Balanced,
|
|
...options,
|
|
});
|
|
});
|
|
},
|
|
async removeWatchAsync(watchId) {
|
|
navigator.geolocation.clearWatch(watchId);
|
|
},
|
|
async watchDeviceHeading(headingId) {
|
|
console.warn('Location.watchDeviceHeading: is not supported on web');
|
|
},
|
|
async hasServicesEnabledAsync() {
|
|
return 'geolocation' in navigator;
|
|
},
|
|
async geocodeAsync() {
|
|
throw new GeocoderError();
|
|
},
|
|
async reverseGeocodeAsync() {
|
|
throw new GeocoderError();
|
|
},
|
|
async watchPositionImplAsync(watchId, options) {
|
|
return new Promise(resolve => {
|
|
// @ts-ignore: the types here need to be fixed
|
|
watchId = global.navigator.geolocation.watchPosition(position => {
|
|
lastKnownPosition = geolocationPositionToJSON(position);
|
|
LocationEventEmitter.emit('Expo.locationChanged', {
|
|
watchId,
|
|
location: lastKnownPosition,
|
|
});
|
|
}, undefined,
|
|
// @ts-ignore: the options object needs to be fixed
|
|
options);
|
|
resolve(watchId);
|
|
});
|
|
},
|
|
getPermissionsAsync,
|
|
async requestPermissionsAsync() {
|
|
return getPermissionsAsync();
|
|
},
|
|
// no-op
|
|
startObserving() { },
|
|
stopObserving() { },
|
|
};
|
|
//# sourceMappingURL=ExpoLocation.web.js.map
|