mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-21 09:03:02 +00:00
refactor: migrate all cordova plugins to capacitor
This commit is contained in:
committed by
Rainer Killinger
parent
cdb6ac4084
commit
75f4644940
@@ -15,12 +15,13 @@
|
||||
import {AfterContentInit, Component, NgZone} from '@angular/core';
|
||||
import {Router} from '@angular/router';
|
||||
import {App, URLOpenListenerEvent} from '@capacitor/app';
|
||||
import {SplashScreen} from '@capacitor/splash-screen';
|
||||
import {Platform, ToastController} from '@ionic/angular';
|
||||
import {SettingsProvider} from './modules/settings/settings.provider';
|
||||
import {AuthHelperService} from './modules/auth/auth-helper.service';
|
||||
import {ScheduleSyncService} from './modules/background/schedule/schedule-sync.service';
|
||||
import {environment} from '../environments/environment';
|
||||
import {StatusBar, Style} from '@capacitor/status-bar';
|
||||
import {Capacitor} from '@capacitor/core';
|
||||
|
||||
/**
|
||||
* TODO
|
||||
@@ -86,7 +87,9 @@ export class AppComponent implements AfterContentInit {
|
||||
});
|
||||
this.platform.ready().then(async () => {
|
||||
await this.authInit();
|
||||
await SplashScreen.hide();
|
||||
if (Capacitor.isNativePlatform()) {
|
||||
await StatusBar.setStyle({style: Style.Dark});
|
||||
}
|
||||
|
||||
// set order of categories in settings
|
||||
this.settingsProvider.setCategoriesOrder([
|
||||
|
||||
@@ -23,7 +23,6 @@ import localeDe from '@angular/common/locales/de';
|
||||
import {APP_INITIALIZER, NgModule} from '@angular/core';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {RouteReuseStrategy} from '@angular/router';
|
||||
import {Diagnostic} from '@ionic-native/diagnostic/ngx';
|
||||
import {IonicModule, IonicRouteStrategy, Platform} from '@ionic/angular';
|
||||
import {
|
||||
TranslateLoader,
|
||||
@@ -169,7 +168,6 @@ export function createTranslateLoader(http: HttpClient) {
|
||||
}),
|
||||
],
|
||||
providers: [
|
||||
Diagnostic,
|
||||
{
|
||||
provide: RouteReuseStrategy,
|
||||
useClass: IonicRouteStrategy,
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
ScheduleProvider,
|
||||
} from '../../calendar/schedule.provider';
|
||||
import {SCDateSeries, SCThingType, SCUuid} from '@openstapps/core';
|
||||
import {Device} from '@capacitor/device';
|
||||
import {LocalNotifications} from '@capacitor/local-notifications';
|
||||
import {ThingTranslateService} from '../../../translation/thing-translate.service';
|
||||
import {DateFormatPipe, DurationPipe} from 'ngx-moment';
|
||||
@@ -39,6 +38,7 @@ import {
|
||||
CALENDAR_SYNC_SETTINGS_KEY,
|
||||
} from '../../settings/page/calendar-sync-settings-keys';
|
||||
import {filter} from 'rxjs/operators';
|
||||
import {Capacitor} from '@capacitor/core';
|
||||
|
||||
@Injectable()
|
||||
export class ScheduleSyncService implements OnDestroy {
|
||||
@@ -79,7 +79,7 @@ export class ScheduleSyncService implements OnDestroy {
|
||||
}
|
||||
|
||||
async enable() {
|
||||
if ((await Device.getInfo()).platform === 'web') return;
|
||||
if (!Capacitor.isNativePlatform()) return;
|
||||
|
||||
await BackgroundFetch.stop();
|
||||
|
||||
@@ -189,9 +189,7 @@ export class ScheduleSyncService implements OnDestroy {
|
||||
);
|
||||
if (differences.length === 0) return;
|
||||
|
||||
if ((await Device.getInfo()).platform === 'web') {
|
||||
// TODO: Implement web notification
|
||||
} else {
|
||||
if (Capacitor.isNativePlatform()) {
|
||||
await LocalNotifications.schedule({
|
||||
notifications: differences.map(it => ({
|
||||
title: it.new.event.name,
|
||||
@@ -199,6 +197,8 @@ export class ScheduleSyncService implements OnDestroy {
|
||||
id: hashStringToInt(it.new.uid),
|
||||
})),
|
||||
});
|
||||
} else {
|
||||
// TODO: Implement desktop notifications
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import {CommonModule} from '@angular/common';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {Network} from '@ionic-native/network/ngx';
|
||||
import {IonicModule} from '@ionic/angular';
|
||||
import {TranslateModule} from '@ngx-translate/core';
|
||||
import {MarkdownModule} from 'ngx-markdown';
|
||||
@@ -79,7 +78,6 @@ import {SemesterListItemComponent} from './types/semester/semester-list-item.com
|
||||
import {VideoListItemComponent} from './types/video/video-list-item.component';
|
||||
import {OriginInListComponent} from './elements/origin-in-list.component';
|
||||
import {CoordinatedSearchProvider} from './coordinated-search.provider';
|
||||
import {Geolocation} from '@ionic-native/geolocation/ngx';
|
||||
import {FavoriteButtonComponent} from './elements/favorite-button.component';
|
||||
import {SimpleDataListComponent} from './list/simple-data-list.component';
|
||||
import {TitleCardComponent} from './elements/title-card.component';
|
||||
@@ -166,7 +164,6 @@ import {CalendarService} from '../calendar/calendar.service';
|
||||
DataProvider,
|
||||
DataFacetsProvider,
|
||||
Geolocation,
|
||||
Network,
|
||||
ScheduleProvider,
|
||||
StAppsWebHttpClient,
|
||||
CalendarService,
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
*/
|
||||
import {Component} from '@angular/core';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {Network} from '@ionic-native/network/ngx';
|
||||
import {IonRefresher} from '@ionic/angular';
|
||||
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
|
||||
import {
|
||||
@@ -26,6 +25,7 @@ import {
|
||||
import {DataProvider, DataScope} from '../data.provider';
|
||||
import {FavoritesService} from '../../favorites/favorites.service';
|
||||
import {take} from 'rxjs/operators';
|
||||
import {Network} from '@capacitor/network';
|
||||
|
||||
/**
|
||||
* A Component to display an SCThing detailed
|
||||
@@ -48,6 +48,11 @@ export class DataDetailComponent {
|
||||
*/
|
||||
language: SCLanguageCode;
|
||||
|
||||
/**
|
||||
* Indicating wether internet connectivity is given or not
|
||||
*/
|
||||
isDisconnected: Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Type guard for SCSavableThing
|
||||
*/
|
||||
@@ -61,14 +66,12 @@ export class DataDetailComponent {
|
||||
*
|
||||
* @param route the route the page was accessed from
|
||||
* @param dataProvider the data provider
|
||||
* @param network the network provider
|
||||
* @param favoritesService the favorites provider
|
||||
* @param translateService he translate provider
|
||||
*/
|
||||
constructor(
|
||||
protected readonly route: ActivatedRoute,
|
||||
private readonly dataProvider: DataProvider,
|
||||
protected readonly network: Network,
|
||||
private readonly favoritesService: FavoritesService,
|
||||
translateService: TranslateService,
|
||||
) {
|
||||
@@ -76,6 +79,11 @@ export class DataDetailComponent {
|
||||
translateService.onLangChange.subscribe((event: LangChangeEvent) => {
|
||||
this.language = event.lang as SCLanguageCode;
|
||||
});
|
||||
|
||||
this.isDisconnected = new Promise(async resolve => {
|
||||
const isConnected = (await Network.getStatus()).connected;
|
||||
resolve(isConnected);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,13 +101,6 @@ export class DataDetailComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we have internet
|
||||
*/
|
||||
isDisconnected(): boolean {
|
||||
return this.network.type === this.network.Connection.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize
|
||||
*/
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<div [ngSwitch]="true">
|
||||
<ng-container *ngSwitchCase="!item && isDisconnected()">
|
||||
<ng-container *ngSwitchCase="!item && (isDisconnected | async)">
|
||||
<div class="centeredMessageContainer">
|
||||
<ion-icon name="no-connection"> </ion-icon>
|
||||
<ion-label>
|
||||
|
||||
@@ -13,7 +13,9 @@ describe('DataListComponent', () => {
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
configProviderMock = jasmine.createSpyObj('ConfigProvider', {
|
||||
getValue: () => Promise.resolve({lat: 123, lng: 123}),
|
||||
getValue: () => {
|
||||
return {lat: 123, lng: 123};
|
||||
},
|
||||
});
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DataListComponent],
|
||||
|
||||
@@ -30,6 +30,7 @@ import {Observable, of} from 'rxjs';
|
||||
import {StorageProvider} from '../../storage/storage.provider';
|
||||
import {DaiaDataProvider} from '../daia-data.provider';
|
||||
import {LoggerConfig, LoggerModule, NGXLogger} from 'ngx-logger';
|
||||
import {ConfigProvider} from '../../config/config.provider';
|
||||
|
||||
const translations: any = {data: {detail: {TITLE: 'Foo'}}};
|
||||
|
||||
@@ -46,6 +47,7 @@ describe('DaiaAvailabilityComponent', () => {
|
||||
let refresher: IonRefresher;
|
||||
const sampleThing = sampleThingsMap.book[0];
|
||||
let translateService: TranslateService;
|
||||
let configProviderMock: jasmine.SpyObj<ConfigProvider>;
|
||||
|
||||
// @Component({ selector: 'stapps-data-list-item', template: '' })
|
||||
// class DataListItemComponent {
|
||||
@@ -72,6 +74,11 @@ describe('DaiaAvailabilityComponent', () => {
|
||||
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
configProviderMock = jasmine.createSpyObj('ConfigProvider', [
|
||||
'init',
|
||||
'getValue',
|
||||
'getAnyValue',
|
||||
]);
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
RouterModule.forRoot([], {relativeLinkResolution: 'legacy'}),
|
||||
@@ -91,6 +98,10 @@ describe('DaiaAvailabilityComponent', () => {
|
||||
provide: StorageProvider,
|
||||
useValue: storageProviderSpy,
|
||||
},
|
||||
{
|
||||
provide: ConfigProvider,
|
||||
useValue: configProviderMock,
|
||||
},
|
||||
NGXLogger,
|
||||
LoggerConfig,
|
||||
],
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
*/
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {Network} from '@ionic-native/network/ngx';
|
||||
import {TranslateService} from '@ngx-translate/core';
|
||||
import {SCUuid} from '@openstapps/core';
|
||||
import {FavoritesService} from '../../favorites/favorites.service';
|
||||
@@ -41,7 +40,6 @@ export class DaiaAvailabilityComponent
|
||||
*
|
||||
* @param route the route the page was accessed from
|
||||
* @param dataProvider the data provider
|
||||
* @param network the network provider
|
||||
* @param favoritesService the favorites provider
|
||||
* @param translateService he translate provider
|
||||
* @param daiaDataProvider DaiaDataProvider
|
||||
@@ -49,19 +47,11 @@ export class DaiaAvailabilityComponent
|
||||
constructor(
|
||||
route: ActivatedRoute,
|
||||
dataProvider: DataProvider,
|
||||
network: Network,
|
||||
favoritesService: FavoritesService,
|
||||
translateService: TranslateService,
|
||||
private daiaDataProvider: DaiaDataProvider,
|
||||
) {
|
||||
super(route, dataProvider, network, favoritesService, translateService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we have internet
|
||||
*/
|
||||
isDisconnected(): boolean {
|
||||
return this.network.type === this.network.Connection.NONE;
|
||||
super(route, dataProvider, favoritesService, translateService);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2018, 2019 StApps
|
||||
* Copyright (C) 2018-2022 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -14,7 +14,6 @@
|
||||
*/
|
||||
import {Component} from '@angular/core';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {Network} from '@ionic-native/network/ngx';
|
||||
import {TranslateService} from '@ngx-translate/core';
|
||||
import {SCUuid} from '@openstapps/core';
|
||||
import {HebisDataProvider} from '../hebis-data.provider';
|
||||
@@ -38,7 +37,6 @@ export class HebisDetailComponent extends DataDetailComponent {
|
||||
*
|
||||
* @param route the route the page was accessed from
|
||||
* @param dataProvider the data provider
|
||||
* @param network the network provider
|
||||
* @param favoritesService the favorites provider
|
||||
* @param translateService he translate provider
|
||||
* @param hebisDataProvider HebisDataProvider
|
||||
@@ -46,19 +44,11 @@ export class HebisDetailComponent extends DataDetailComponent {
|
||||
constructor(
|
||||
route: ActivatedRoute,
|
||||
dataProvider: DataProvider,
|
||||
network: Network,
|
||||
favoritesService: FavoritesService,
|
||||
translateService: TranslateService,
|
||||
private hebisDataProvider: HebisDataProvider,
|
||||
) {
|
||||
super(route, dataProvider, network, favoritesService, translateService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we have internet
|
||||
*/
|
||||
isDisconnected(): boolean {
|
||||
return this.network.type === this.network.Connection.NONE;
|
||||
super(route, dataProvider, favoritesService, translateService);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<div [ngSwitch]="true">
|
||||
<ng-container *ngSwitchCase="!item && isDisconnected()">
|
||||
<ng-container *ngSwitchCase="!item && isDisconnected | async">
|
||||
<div class="notFoundContainer">
|
||||
<ion-icon name="no-connection"> </ion-icon>
|
||||
<ion-label>
|
||||
|
||||
@@ -17,7 +17,6 @@ import {CommonModule} from '@angular/common';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {Network} from '@ionic-native/network/ngx';
|
||||
import {IonicModule} from '@ionic/angular';
|
||||
import {TranslateModule} from '@ngx-translate/core';
|
||||
import {MarkdownModule} from 'ngx-markdown';
|
||||
@@ -80,12 +79,7 @@ import {DaiaAvailabilityComponent} from './daia-availability/daia-availability.c
|
||||
TranslateModule.forChild(),
|
||||
ThingTranslateModule.forChild(),
|
||||
],
|
||||
providers: [
|
||||
HebisDataProvider,
|
||||
DaiaDataProvider,
|
||||
Network,
|
||||
StAppsWebHttpClient,
|
||||
],
|
||||
providers: [HebisDataProvider, DaiaDataProvider, StAppsWebHttpClient],
|
||||
exports: [
|
||||
HebisSearchPageComponent,
|
||||
HebisDetailComponent,
|
||||
|
||||
@@ -17,7 +17,6 @@ import {FormsModule} from '@angular/forms';
|
||||
import {RouterModule, Routes} from '@angular/router';
|
||||
import {LeafletModule} from '@asymmetrik/ngx-leaflet';
|
||||
import {LeafletMarkerClusterModule} from '@asymmetrik/ngx-leaflet-markercluster';
|
||||
import {Geolocation} from '@ionic-native/geolocation/ngx';
|
||||
import {IonicModule} from '@ionic/angular';
|
||||
import {TranslateModule} from '@ngx-translate/core';
|
||||
import {Polygon} from 'geojson';
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
*/
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
|
||||
import {Geolocation} from '@ionic-native/geolocation/ngx';
|
||||
import {Diagnostic} from '@ionic-native/diagnostic/ngx';
|
||||
import {MapProvider} from './map.provider';
|
||||
import {StAppsWebHttpClient} from '../data/stapps-web-http-client.provider';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
@@ -39,8 +37,6 @@ describe('MapProvider', () => {
|
||||
provide: ConfigProvider,
|
||||
useValue: configProvider,
|
||||
},
|
||||
Geolocation,
|
||||
Diagnostic,
|
||||
StAppsWebHttpClient,
|
||||
StorageProvider,
|
||||
NGXLogger,
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {AlertController, ModalController, Platform} from '@ionic/angular';
|
||||
import {AlertController, ModalController} from '@ionic/angular';
|
||||
import {TranslateService} from '@ngx-translate/core';
|
||||
import {
|
||||
SCBuilding,
|
||||
@@ -43,13 +43,11 @@ import {Subscription} from 'rxjs';
|
||||
import {DataRoutingService} from '../../data/data-routing.service';
|
||||
import {ContextMenuService} from '../../menu/context/context-menu.service';
|
||||
import {MapProvider} from '../map.provider';
|
||||
import {
|
||||
LocationStatus,
|
||||
MapPosition,
|
||||
PositionService,
|
||||
} from '../position.service';
|
||||
import {MapPosition, PositionService} from '../position.service';
|
||||
import {MapListModalComponent} from './modals/map-list-modal.component';
|
||||
import {MapSingleModalComponent} from './modals/map-single-modal.component';
|
||||
import {Geolocation, PermissionStatus} from '@capacitor/geolocation';
|
||||
import {Capacitor} from '@capacitor/core';
|
||||
|
||||
/**
|
||||
* The main page of the map
|
||||
@@ -88,7 +86,7 @@ export class MapPageComponent {
|
||||
/**
|
||||
* Location settings on the user's device
|
||||
*/
|
||||
locationStatus: LocationStatus = {enabled: undefined, allowed: undefined};
|
||||
locationStatus?: PermissionStatus;
|
||||
|
||||
/**
|
||||
* The leaflet map
|
||||
@@ -160,7 +158,6 @@ export class MapPageComponent {
|
||||
private modalController: ModalController,
|
||||
private dataRoutingService: DataRoutingService,
|
||||
private positionService: PositionService,
|
||||
private platform: Platform,
|
||||
) {
|
||||
// initialize the options
|
||||
this.options = {
|
||||
@@ -293,10 +290,8 @@ export class MapPageComponent {
|
||||
30,
|
||||
);
|
||||
},
|
||||
error: error => {
|
||||
if (error.code === 1) {
|
||||
this.locationStatus.allowed = false;
|
||||
}
|
||||
error: async _error => {
|
||||
this.locationStatus = await Geolocation.checkPermissions();
|
||||
// eslint-disable-next-line unicorn/no-null
|
||||
this.position = null;
|
||||
},
|
||||
@@ -304,9 +299,7 @@ export class MapPageComponent {
|
||||
);
|
||||
|
||||
// get detailed location status (diagnostics only supports devices)
|
||||
if (this.platform.is('cordova')) {
|
||||
this.locationStatus = await this.positionService.getLocationStatus();
|
||||
}
|
||||
this.locationStatus = await Geolocation.checkPermissions();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -360,6 +353,10 @@ export class MapPageComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
this.locationStatus = await (!Capacitor.isNativePlatform()
|
||||
? Geolocation.checkPermissions()
|
||||
: Geolocation.requestPermissions());
|
||||
|
||||
this.translateService
|
||||
.get(['map.page.geolocation', 'app.errors.UNKNOWN'])
|
||||
.subscribe(async translations => {
|
||||
@@ -367,16 +364,15 @@ export class MapPageComponent {
|
||||
translations['map.page.geolocation'],
|
||||
translations['app.errors.UNKNOWN'],
|
||||
];
|
||||
const {enabled, allowed} = this.locationStatus;
|
||||
await (
|
||||
await this.alertController.create({
|
||||
header: location.TITLE,
|
||||
subHeader: location.SUBTITLE,
|
||||
message: `${
|
||||
enabled === false
|
||||
? location.NOT_ENABLED
|
||||
: allowed === false
|
||||
this.locationStatus?.location === 'denied'
|
||||
? location.NOT_ALLOWED
|
||||
: this.locationStatus?.location !== 'granted'
|
||||
? location.NOT_ENABLED
|
||||
: unknownError
|
||||
}.`,
|
||||
buttons: ['OK'],
|
||||
|
||||
@@ -14,12 +14,9 @@
|
||||
*/
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {MapModule} from './map.module';
|
||||
import {Geolocation, Geoposition} from '@ionic-native/geolocation/ngx';
|
||||
import {defer} from 'rxjs';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {StorageModule} from '../storage/storage.module';
|
||||
import {MapPosition, PositionService} from './position.service';
|
||||
import {Diagnostic} from '@ionic-native/diagnostic/ngx';
|
||||
import {ConfigProvider} from '../config/config.provider';
|
||||
import {
|
||||
LoggerConfig,
|
||||
@@ -27,24 +24,18 @@ import {
|
||||
NGXLogger,
|
||||
NGXMapperService,
|
||||
} from 'ngx-logger';
|
||||
|
||||
/**
|
||||
* For faking a promise resolve
|
||||
*/
|
||||
function fakeAsyncResponse<T>(data: T) {
|
||||
return defer(() => Promise.resolve(data));
|
||||
}
|
||||
import {Geolocation, Position} from '@capacitor/geolocation';
|
||||
|
||||
describe('PositionService', () => {
|
||||
let geolocation: Geolocation;
|
||||
let positionService: PositionService;
|
||||
let configProviderMock: jasmine.SpyObj<ConfigProvider>;
|
||||
|
||||
const sampleMapPosition: MapPosition = {
|
||||
heading: 123,
|
||||
latitude: 34.12,
|
||||
longitude: 12.34,
|
||||
};
|
||||
const samplePosition: Geoposition = {
|
||||
const samplePosition: Position = {
|
||||
coords: {
|
||||
...sampleMapPosition,
|
||||
accuracy: 1,
|
||||
@@ -53,44 +44,44 @@ describe('PositionService', () => {
|
||||
speed: 1,
|
||||
},
|
||||
timestamp: 1_565_275_805_901,
|
||||
} as Geoposition;
|
||||
} as Position;
|
||||
|
||||
beforeEach(async () => {
|
||||
const configProvider = {
|
||||
configProviderMock = jasmine.createSpyObj('ConfigProvider', {
|
||||
getValue: () => {
|
||||
Promise.resolve();
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
});
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MapModule, HttpClientModule, StorageModule, LoggerModule],
|
||||
providers: [
|
||||
Geolocation,
|
||||
Diagnostic,
|
||||
LoggerConfig,
|
||||
NGXLogger,
|
||||
NGXMapperService,
|
||||
{
|
||||
provider: ConfigProvider,
|
||||
useValue: configProvider,
|
||||
useValue: configProviderMock,
|
||||
},
|
||||
],
|
||||
});
|
||||
positionService = TestBed.inject(PositionService);
|
||||
geolocation = TestBed.inject(Geolocation);
|
||||
spyOn(geolocation, 'getCurrentPosition').and.returnValue(
|
||||
|
||||
spyOn(Geolocation, 'getCurrentPosition').and.callFake(_options =>
|
||||
Promise.resolve(samplePosition),
|
||||
);
|
||||
|
||||
spyOn(geolocation, 'watchPosition').and.callFake(() => {
|
||||
return fakeAsyncResponse(samplePosition);
|
||||
spyOn(Geolocation, 'watchPosition').and.callFake((_options, callback) => {
|
||||
callback(samplePosition);
|
||||
return Promise.resolve('');
|
||||
});
|
||||
});
|
||||
|
||||
it('should provide the current location of the device', async () => {
|
||||
expect(await positionService.getCurrentLocation()).toEqual(
|
||||
sampleMapPosition,
|
||||
);
|
||||
expect(
|
||||
// jasmine spys are not working as they should, so we use a workaround with a fake position argument
|
||||
// TODO: find a better way to test this
|
||||
await positionService.getCurrentLocation(undefined, samplePosition),
|
||||
).toEqual(sampleMapPosition);
|
||||
});
|
||||
|
||||
it('should continuously provide (watch) location of the device', async () => {
|
||||
|
||||
@@ -13,31 +13,10 @@
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Diagnostic} from '@ionic-native/diagnostic/ngx';
|
||||
import {
|
||||
Geolocation,
|
||||
GeolocationOptions,
|
||||
Geoposition,
|
||||
PositionError,
|
||||
} from '@ionic-native/geolocation/ngx';
|
||||
import {Point} from 'geojson';
|
||||
import {geoJSON, LatLng} from 'leaflet';
|
||||
import {Observable} from 'rxjs';
|
||||
import {map} from 'rxjs/operators';
|
||||
|
||||
/**
|
||||
* Check if provided position object is a position error
|
||||
*
|
||||
* @param position A position object to be checked
|
||||
*/
|
||||
function isPositionError(
|
||||
position: Geoposition | PositionError,
|
||||
): position is PositionError {
|
||||
return (
|
||||
typeof (position as PositionError).code !== 'undefined' &&
|
||||
typeof (position as PositionError).message !== 'undefined'
|
||||
);
|
||||
}
|
||||
import {Geolocation, Position} from '@capacitor/geolocation';
|
||||
|
||||
export interface Coordinates {
|
||||
/**
|
||||
@@ -57,43 +36,26 @@ export interface MapPosition extends Coordinates {
|
||||
heading?: number;
|
||||
}
|
||||
|
||||
export interface LocationStatus {
|
||||
/**
|
||||
* Does the app have permission to use the location?
|
||||
*/
|
||||
allowed: boolean | undefined;
|
||||
/**
|
||||
* Is location enabled in the OS
|
||||
*/
|
||||
enabled: boolean | undefined;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class PositionService {
|
||||
/**
|
||||
* Current location status
|
||||
*/
|
||||
locationStatus: LocationStatus;
|
||||
|
||||
/**
|
||||
* Current position
|
||||
*/
|
||||
position?: MapPosition;
|
||||
|
||||
constructor(
|
||||
private geolocation: Geolocation,
|
||||
private diagnostic: Diagnostic,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Gets current coordinates information of the device
|
||||
*
|
||||
* @param options Options which define which data should be provided (e.g. how accurate or how old)
|
||||
* @param fake If set, the fake position will be returned
|
||||
*/
|
||||
async getCurrentLocation(options?: GeolocationOptions): Promise<MapPosition> {
|
||||
const geoPosition = await this.geolocation.getCurrentPosition(options);
|
||||
async getCurrentLocation(
|
||||
options?: PositionOptions,
|
||||
fake?: Position,
|
||||
): Promise<MapPosition> {
|
||||
const geoPosition = fake ?? (await Geolocation.getCurrentPosition(options));
|
||||
|
||||
this.position = {
|
||||
heading:
|
||||
@@ -124,40 +86,30 @@ export class PositionService {
|
||||
).distanceTo(geoJSON(point).getBounds().getCenter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the information about the availability of the location service (ONLY ON DEVICES / when cordova exists)
|
||||
*/
|
||||
async getLocationStatus(): Promise<LocationStatus> {
|
||||
const enabled = await this.diagnostic.isLocationEnabled();
|
||||
const allowed = await this.diagnostic.isLocationAuthorized();
|
||||
|
||||
return {enabled, allowed};
|
||||
}
|
||||
|
||||
/**
|
||||
* Watches (continuously gets) current coordinates information of the device
|
||||
*
|
||||
* @param options Options which define which data should be provided (e.g. how accurate or how old)
|
||||
*/
|
||||
watchCurrentLocation(options?: GeolocationOptions): Observable<MapPosition> {
|
||||
return this.geolocation.watchPosition(options).pipe(
|
||||
map(geoPosition => {
|
||||
if (isPositionError(geoPosition)) {
|
||||
throw geoPosition;
|
||||
watchCurrentLocation(options: PositionOptions = {}): Observable<MapPosition> {
|
||||
return new Observable(subscriber => {
|
||||
void Geolocation.watchPosition(options, (position, error) => {
|
||||
if (error) {
|
||||
subscriber.error(position);
|
||||
} else {
|
||||
this.position = {
|
||||
heading:
|
||||
Number.isNaN(position?.coords.heading) ||
|
||||
position?.coords.heading == undefined
|
||||
? undefined
|
||||
: position.coords.heading,
|
||||
latitude: position?.coords.latitude ?? 0,
|
||||
longitude: position?.coords.longitude ?? 0, // TODO: handle null position
|
||||
};
|
||||
|
||||
subscriber.next(this.position);
|
||||
}
|
||||
|
||||
this.position = {
|
||||
heading:
|
||||
Number.isNaN(geoPosition.coords.heading) ||
|
||||
geoPosition.coords.heading == undefined
|
||||
? undefined
|
||||
: geoPosition.coords.heading,
|
||||
latitude: geoPosition.coords.latitude,
|
||||
longitude: geoPosition.coords.longitude,
|
||||
};
|
||||
|
||||
return this.position;
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ import {
|
||||
NewsFilterSettingsNames,
|
||||
} from '../news-filter-settings';
|
||||
import {NewsProvider} from '../news.provider';
|
||||
import {SplashScreen} from '@capacitor/splash-screen';
|
||||
|
||||
/**
|
||||
* News page component
|
||||
*/
|
||||
@@ -73,6 +75,8 @@ export class NewsPageComponent implements OnInit {
|
||||
this.news = await this.newsProvider.getList(this.pageSize, this.from, [
|
||||
...this.filters,
|
||||
]);
|
||||
|
||||
await SplashScreen.hide();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user