mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-21 00:52:55 +00:00
fix: location flow on iOS devices
This commit is contained in:
@@ -13,7 +13,9 @@
|
|||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
|
import {MapPosition} from '../../map/position.service';
|
||||||
import {SearchPageComponent} from './search-page.component';
|
import {SearchPageComponent} from './search-page.component';
|
||||||
|
import {Geolocation} from '@capacitor/geolocation';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Presents a list of places for eating/drinking
|
* Presents a list of places for eating/drinking
|
||||||
@@ -91,4 +93,28 @@ export class FoodDataListComponent extends SearchPageComponent {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async ionViewWillEnter() {
|
||||||
|
await super.ionViewWillEnter();
|
||||||
|
this.subscriptions.push(
|
||||||
|
this.positionService
|
||||||
|
.watchCurrentLocation(this.constructor.name, {enableHighAccuracy: false, maximumAge: 1000})
|
||||||
|
.subscribe({
|
||||||
|
next: (position: MapPosition) => {
|
||||||
|
this.positionService.position = position;
|
||||||
|
},
|
||||||
|
error: async _error => {
|
||||||
|
this.positionService.position = undefined;
|
||||||
|
await Geolocation.checkPermissions();
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ionViewWillLeave() {
|
||||||
|
void this.positionService.clearWatcher(this.constructor.name);
|
||||||
|
for (const sub of this.subscriptions) {
|
||||||
|
sub.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -260,6 +260,11 @@ export class MapPageComponent {
|
|||||||
* Subscribe to needed observables and get the location status when user is entering the page
|
* Subscribe to needed observables and get the location status when user is entering the page
|
||||||
*/
|
*/
|
||||||
async ionViewWillEnter() {
|
async ionViewWillEnter() {
|
||||||
|
if (this.positionService.position) {
|
||||||
|
this.position = this.positionService.position;
|
||||||
|
this.positionMarker = MapProvider.getPositionMarker(this.position, 'stapps-device-location', 32);
|
||||||
|
}
|
||||||
|
|
||||||
this.subscriptions.push(
|
this.subscriptions.push(
|
||||||
this.dataRoutingService.itemSelectListener().subscribe(async item => {
|
this.dataRoutingService.itemSelectListener().subscribe(async item => {
|
||||||
// in case the list item is clicked
|
// in case the list item is clicked
|
||||||
@@ -269,17 +274,19 @@ export class MapPageComponent {
|
|||||||
void this.router.navigate(['/data-detail', item.uid]);
|
void this.router.navigate(['/data-detail', item.uid]);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
this.positionService.watchCurrentLocation({maximumAge: 3000}).subscribe({
|
this.positionService
|
||||||
next: (position: MapPosition) => {
|
.watchCurrentLocation(this.constructor.name, {enableHighAccuracy: true, maximumAge: 1000})
|
||||||
this.position = position;
|
.subscribe({
|
||||||
this.positionMarker = MapProvider.getPositionMarker(position, 'stapps-device-location', 32);
|
next: (position: MapPosition) => {
|
||||||
},
|
this.position = position;
|
||||||
error: async _error => {
|
this.positionMarker = MapProvider.getPositionMarker(position, 'stapps-device-location', 32);
|
||||||
this.locationStatus = await Geolocation.checkPermissions();
|
},
|
||||||
// eslint-disable-next-line unicorn/no-null
|
error: async _error => {
|
||||||
this.position = null;
|
this.locationStatus = await Geolocation.checkPermissions();
|
||||||
},
|
// eslint-disable-next-line unicorn/no-null
|
||||||
}),
|
this.position = null;
|
||||||
|
},
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// get detailed location status (diagnostics only supports devices)
|
// get detailed location status (diagnostics only supports devices)
|
||||||
@@ -290,6 +297,7 @@ export class MapPageComponent {
|
|||||||
* Unsubscribe from all subscriptions when user leaves page
|
* Unsubscribe from all subscriptions when user leaves page
|
||||||
*/
|
*/
|
||||||
ionViewWillLeave() {
|
ionViewWillLeave() {
|
||||||
|
void this.positionService.clearWatcher(this.constructor.name);
|
||||||
for (const sub of this.subscriptions) {
|
for (const sub of this.subscriptions) {
|
||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,12 +96,12 @@
|
|||||||
<ion-icon *ngIf="position !== null; else noLocationIcon" name="my_location"></ion-icon>
|
<ion-icon *ngIf="position !== null; else noLocationIcon" name="my_location"></ion-icon>
|
||||||
<ng-template #noLocationIcon>
|
<ng-template #noLocationIcon>
|
||||||
<ion-icon
|
<ion-icon
|
||||||
*ngIf="locationStatus.location !== 'denied'; else deniedLocationIcon"
|
*ngIf="locationStatus && locationStatus.location === 'denied'; else pendingLocationIcon"
|
||||||
name="location_searching"
|
name="location_disabled"
|
||||||
></ion-icon>
|
></ion-icon>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template #deniedLocationIcon>
|
<ng-template #pendingLocationIcon>
|
||||||
<ion-icon name="location_disabled"></ion-icon>
|
<ion-icon name="location_searching"></ion-icon>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -58,9 +58,30 @@ describe('PositionService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should continuously provide (watch) location of the device', done => {
|
it('should continuously provide (watch) location of the device', done => {
|
||||||
positionService.watchCurrentLocation().subscribe(location => {
|
positionService.watchCurrentLocation('testCaller').subscribe(location => {
|
||||||
expect(location).toBeDefined();
|
expect(location).toBeDefined();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should stop to continuously provide (watch) location of the device', done => {
|
||||||
|
positionService.watchers.set(
|
||||||
|
'clearWatch',
|
||||||
|
new Promise(resolve => {
|
||||||
|
setTimeout(function () {
|
||||||
|
resolve(`watcherID123`);
|
||||||
|
}, 20);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
positionService
|
||||||
|
.clearWatcher('clearWatch')
|
||||||
|
.then(result => {
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -45,6 +45,11 @@ export class PositionService {
|
|||||||
*/
|
*/
|
||||||
position?: MapPosition;
|
position?: MapPosition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of callers and their running watchers. Both by their ID
|
||||||
|
*/
|
||||||
|
watchers: Map<string, Promise<string>> = new Map();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets current coordinates information of the device
|
* Gets current coordinates information of the device
|
||||||
*
|
*
|
||||||
@@ -84,19 +89,19 @@ export class PositionService {
|
|||||||
/**
|
/**
|
||||||
* Watches (continuously gets) current coordinates information of the device
|
* Watches (continuously gets) current coordinates information of the device
|
||||||
*
|
*
|
||||||
|
* @param caller Identifier for later reference. (I.e use of `clearWatcher`)
|
||||||
* @param options Options which define which data should be provided (e.g. how accurate or how old)
|
* @param options Options which define which data should be provided (e.g. how accurate or how old)
|
||||||
*/
|
*/
|
||||||
watchCurrentLocation(options: PositionOptions = {}): Observable<MapPosition> {
|
watchCurrentLocation(caller: string, options: PositionOptions = {}): Observable<MapPosition> {
|
||||||
return new Observable(subscriber => {
|
return new Observable(subscriber => {
|
||||||
void Geolocation.watchPosition(options, (position, error) => {
|
const watcherID = Geolocation.watchPosition(options, (position, error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
subscriber.error(position);
|
subscriber.error(position);
|
||||||
} else {
|
} else {
|
||||||
this.position = {
|
this.position = {
|
||||||
heading:
|
// TODO use native compass heading instead
|
||||||
Number.isNaN(position?.coords.heading) || position?.coords.heading == undefined
|
// waiting for https://github.com/ionic-team/capacitor-plugins/issues/1192
|
||||||
? undefined
|
heading: undefined,
|
||||||
: position.coords.heading,
|
|
||||||
latitude: position?.coords.latitude ?? 0,
|
latitude: position?.coords.latitude ?? 0,
|
||||||
longitude: position?.coords.longitude ?? 0, // TODO: handle null position
|
longitude: position?.coords.longitude ?? 0, // TODO: handle null position
|
||||||
};
|
};
|
||||||
@@ -104,6 +109,19 @@ export class PositionService {
|
|||||||
subscriber.next(this.position);
|
subscriber.next(this.position);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.watchers.set(caller, watcherID);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears watcher for a certain caller
|
||||||
|
*
|
||||||
|
* @param caller Identifier of the caller wanting to clear the watcher
|
||||||
|
*/
|
||||||
|
async clearWatcher(caller: string): Promise<void> {
|
||||||
|
const watcherID = await this.watchers.get(caller);
|
||||||
|
if (watcherID) {
|
||||||
|
Geolocation.clearWatch({id: watcherID});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user