Live location sharing - handle geolocation errors (#8179)

* display live share warning only when geolocation is happening

Signed-off-by: Kerry Archibald <kerrya@element.io>

* kill beacons when geolocation is unavailable or permissions denied

Signed-off-by: Kerry Archibald <kerrya@element.io>

* polish and comments

Signed-off-by: Kerry Archibald <kerrya@element.io>
This commit is contained in:
Kerry 2022-03-28 18:46:39 +02:00 committed by GitHub
parent 2520d81784
commit d2b97e251e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 287 additions and 60 deletions

View file

@ -44,6 +44,7 @@ const isOwnBeacon = (beacon: Beacon, userId: string): boolean => beacon.beaconIn
export enum OwnBeaconStoreEvent {
LivenessChange = 'OwnBeaconStore.LivenessChange',
MonitoringLivePosition = 'OwnBeaconStore.MonitoringLivePosition',
}
const MOVING_UPDATE_INTERVAL = 2000;
@ -232,18 +233,28 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
await this.matrixClient.unstable_setLiveBeacon(beacon.roomId, beacon.beaconInfoEventType, updateContent);
};
private togglePollingLocation = async (): Promise<void> => {
private togglePollingLocation = () => {
if (!!this.liveBeaconIds.length) {
return this.startPollingLocation();
this.startPollingLocation();
} else {
this.stopPollingLocation();
}
return this.stopPollingLocation();
};
private startPollingLocation = async () => {
// clear any existing interval
this.stopPollingLocation();
this.clearPositionWatch = await watchPosition(this.onWatchedPosition, this.onWatchedPositionError);
try {
this.clearPositionWatch = await watchPosition(
this.onWatchedPosition,
this.onGeolocationError,
);
} catch (error) {
this.onGeolocationError(error?.message);
// don't set locationInterval if geolocation failed to setup
return;
}
this.locationInterval = setInterval(() => {
if (!this.lastPublishedPositionTimestamp) {
@ -255,6 +266,8 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
this.publishCurrentLocationToBeacons();
}
}, STATIC_UPDATE_INTERVAL);
this.emit(OwnBeaconStoreEvent.MonitoringLivePosition);
};
private onWatchedPosition = (position: GeolocationPosition) => {
@ -268,11 +281,6 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
}
};
private onWatchedPositionError = (error: GeolocationError) => {
this.geolocationError = error;
logger.error(this.geolocationError);
};
private stopPollingLocation = () => {
clearInterval(this.locationInterval);
this.locationInterval = undefined;
@ -283,6 +291,8 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
this.clearPositionWatch();
this.clearPositionWatch = undefined;
}
this.emit(OwnBeaconStoreEvent.MonitoringLivePosition);
};
/**
@ -313,8 +323,31 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
* and publishes it to all live beacons
*/
private publishCurrentLocationToBeacons = async () => {
const position = await getCurrentPosition();
// TODO error handling
this.publishLocationToBeacons(mapGeolocationPositionToTimedGeo(position));
try {
const position = await getCurrentPosition();
// TODO error handling
this.publishLocationToBeacons(mapGeolocationPositionToTimedGeo(position));
} catch (error) {
this.onGeolocationError(error?.message);
}
};
private onGeolocationError = async (error: GeolocationError): Promise<void> => {
this.geolocationError = error;
logger.error('Geolocation failed', this.geolocationError);
// other errors are considered non-fatal
// and self recovering
if (![
GeolocationError.Unavailable,
GeolocationError.PermissionDenied,
].includes(error)) {
return;
}
this.stopPollingLocation();
// kill live beacons when location permissions are revoked
// TODO may need adjustment when PSF-797 is done
await Promise.all(this.liveBeaconIds.map(this.stopBeacon));
};
}