This repository has been archived on 2022-03-12. You can view files and clone it, but cannot push or open issues or pull requests.
2021-04-02 02:24:13 +03:00

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