mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-03-15 19:22:27 +00:00
feat: use takeUntilDestroy instead of manually unsubscribing
This commit is contained in:
committed by
Rainer Killinger
parent
bebee6b4d0
commit
06f3120345
@@ -13,21 +13,23 @@
|
|||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Component, Input, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
import {Component, DestroyRef, inject, Input, OnInit, ViewChild} from '@angular/core';
|
||||||
import {ActivatedRoute} from '@angular/router';
|
import {ActivatedRoute} from '@angular/router';
|
||||||
import {AssessmentsProvider} from '../assessments.provider';
|
import {AssessmentsProvider} from '../assessments.provider';
|
||||||
import {DataDetailComponent, ExternalDataLoadEvent} from '../../data/detail/data-detail.component';
|
import {DataDetailComponent, ExternalDataLoadEvent} from '../../data/detail/data-detail.component';
|
||||||
import {NavController, ViewWillEnter} from '@ionic/angular';
|
import {NavController, ViewWillEnter} from '@ionic/angular';
|
||||||
import {Subscription} from 'rxjs';
|
|
||||||
import {DataRoutingService} from '../../data/data-routing.service';
|
import {DataRoutingService} from '../../data/data-routing.service';
|
||||||
import {SCAssessment} from '@openstapps/core';
|
import {SCAssessment} from '@openstapps/core';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'assessments-detail',
|
selector: 'assessments-detail',
|
||||||
templateUrl: 'assessments-detail.html',
|
templateUrl: 'assessments-detail.html',
|
||||||
styleUrls: ['assessments-detail.scss'],
|
styleUrls: ['assessments-detail.scss'],
|
||||||
})
|
})
|
||||||
export class AssessmentsDetailComponent implements ViewWillEnter, OnInit, OnDestroy {
|
export class AssessmentsDetailComponent implements ViewWillEnter, OnInit {
|
||||||
|
destroy$ = inject(DestroyRef);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly route: ActivatedRoute,
|
readonly route: ActivatedRoute,
|
||||||
readonly assessmentsProvider: AssessmentsProvider,
|
readonly assessmentsProvider: AssessmentsProvider,
|
||||||
@@ -36,8 +38,6 @@ export class AssessmentsDetailComponent implements ViewWillEnter, OnInit, OnDest
|
|||||||
readonly activatedRoute: ActivatedRoute,
|
readonly activatedRoute: ActivatedRoute,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
subscriptions: Subscription[] = [];
|
|
||||||
|
|
||||||
@Input() dataPathAutoRouting = true;
|
@Input() dataPathAutoRouting = true;
|
||||||
|
|
||||||
@ViewChild(DataDetailComponent)
|
@ViewChild(DataDetailComponent)
|
||||||
@@ -47,19 +47,16 @@ export class AssessmentsDetailComponent implements ViewWillEnter, OnInit, OnDest
|
|||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
if (!this.dataPathAutoRouting) return;
|
if (!this.dataPathAutoRouting) return;
|
||||||
this.subscriptions.push(
|
this.dataRoutingService
|
||||||
this.dataRoutingService.pathSelectListener().subscribe(item => {
|
.pathSelectListener()
|
||||||
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
|
.subscribe(item => {
|
||||||
void this.navController.navigateBack(['assessments', 'detail', item.uid], {
|
void this.navController.navigateBack(['assessments', 'detail', item.uid], {
|
||||||
queryParams: {
|
queryParams: {
|
||||||
token: this.activatedRoute.snapshot.queryParamMap.get('token'),
|
token: this.activatedRoute.snapshot.queryParamMap.get('token'),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
for (const sub of this.subscriptions) sub.unsubscribe();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getItem(event: ExternalDataLoadEvent) {
|
getItem(event: ExternalDataLoadEvent) {
|
||||||
|
|||||||
@@ -13,18 +13,18 @@
|
|||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
|
import {Component, DestroyRef, inject, Input, OnInit} from '@angular/core';
|
||||||
import {SCThings} from '@openstapps/core';
|
import {SCThings} from '@openstapps/core';
|
||||||
import {Subscription} from 'rxjs';
|
|
||||||
import {DataRoutingService} from '../../data/data-routing.service';
|
import {DataRoutingService} from '../../data/data-routing.service';
|
||||||
import {ActivatedRoute, Router} from '@angular/router';
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'assessments-simple-data-list',
|
selector: 'assessments-simple-data-list',
|
||||||
templateUrl: 'assessments-simple-data-list.html',
|
templateUrl: 'assessments-simple-data-list.html',
|
||||||
styleUrls: ['assessments-simple-data-list.scss'],
|
styleUrls: ['assessments-simple-data-list.scss'],
|
||||||
})
|
})
|
||||||
export class AssessmentsSimpleDataListComponent implements OnInit, OnDestroy {
|
export class AssessmentsSimpleDataListComponent implements OnInit {
|
||||||
/**
|
/**
|
||||||
* All SCThings to display
|
* All SCThings to display
|
||||||
*/
|
*/
|
||||||
@@ -44,7 +44,7 @@ export class AssessmentsSimpleDataListComponent implements OnInit, OnDestroy {
|
|||||||
this._items = new Promise(resolve => resolve(items));
|
this._items = new Promise(resolve => resolve(items));
|
||||||
}
|
}
|
||||||
|
|
||||||
subscriptions: Subscription[] = [];
|
destroy$ = inject(DestroyRef);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly dataRoutingService: DataRoutingService,
|
readonly dataRoutingService: DataRoutingService,
|
||||||
@@ -53,18 +53,15 @@ export class AssessmentsSimpleDataListComponent implements OnInit, OnDestroy {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.subscriptions.push(
|
this.dataRoutingService
|
||||||
this.dataRoutingService.itemSelectListener().subscribe(thing => {
|
.itemSelectListener()
|
||||||
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
|
.subscribe(thing => {
|
||||||
void this.router.navigate(['assessments', 'detail', thing.uid], {
|
void this.router.navigate(['assessments', 'detail', thing.uid], {
|
||||||
queryParams: {
|
queryParams: {
|
||||||
token: this.activatedRoute.snapshot.queryParamMap.get('token'),
|
token: this.activatedRoute.snapshot.queryParamMap.get('token'),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
for (const subscription of this.subscriptions) subscription.unsubscribe();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,17 +12,17 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {AfterViewInit, Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
import {AfterViewInit, Component, DestroyRef, inject, OnInit, ViewChild} from '@angular/core';
|
||||||
import {AssessmentsProvider} from '../assessments.provider';
|
import {AssessmentsProvider} from '../assessments.provider';
|
||||||
import {SCAssessment, SCCourseOfStudy} from '@openstapps/core';
|
import {SCAssessment, SCCourseOfStudy} from '@openstapps/core';
|
||||||
import {ActivatedRoute, Router} from '@angular/router';
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
import {Subscription} from 'rxjs';
|
|
||||||
import {NGXLogger} from 'ngx-logger';
|
import {NGXLogger} from 'ngx-logger';
|
||||||
import {materialSharedAxisX} from '../../../animation/material-motion';
|
import {materialSharedAxisX} from '../../../animation/material-motion';
|
||||||
import {SharedAxisChoreographer} from '../../../animation/animation-choreographer';
|
import {SharedAxisChoreographer} from '../../../animation/animation-choreographer';
|
||||||
import {DataProvider, DataScope} from '../../data/data.provider';
|
import {DataProvider, DataScope} from '../../data/data.provider';
|
||||||
import {DataRoutingService} from '../../data/data-routing.service';
|
import {DataRoutingService} from '../../data/data-routing.service';
|
||||||
import {groupBy, mapValues} from '@openstapps/collection-utils';
|
import {groupBy, mapValues} from '@openstapps/collection-utils';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-assessments-page',
|
selector: 'app-assessments-page',
|
||||||
@@ -30,7 +30,7 @@ import {groupBy, mapValues} from '@openstapps/collection-utils';
|
|||||||
styleUrls: ['assessments-page.scss'],
|
styleUrls: ['assessments-page.scss'],
|
||||||
animations: [materialSharedAxisX],
|
animations: [materialSharedAxisX],
|
||||||
})
|
})
|
||||||
export class AssessmentsPageComponent implements OnInit, AfterViewInit, OnDestroy {
|
export class AssessmentsPageComponent implements OnInit, AfterViewInit {
|
||||||
assessments: Promise<
|
assessments: Promise<
|
||||||
Record<
|
Record<
|
||||||
string,
|
string,
|
||||||
@@ -43,12 +43,12 @@ export class AssessmentsPageComponent implements OnInit, AfterViewInit, OnDestro
|
|||||||
|
|
||||||
assessmentKeys: string[] = [];
|
assessmentKeys: string[] = [];
|
||||||
|
|
||||||
routingSubscription: Subscription;
|
|
||||||
|
|
||||||
@ViewChild('segment') segmentView!: HTMLIonSegmentElement;
|
@ViewChild('segment') segmentView!: HTMLIonSegmentElement;
|
||||||
|
|
||||||
sharedAxisChoreographer: SharedAxisChoreographer<string> = new SharedAxisChoreographer<string>('', []);
|
sharedAxisChoreographer: SharedAxisChoreographer<string> = new SharedAxisChoreographer<string>('', []);
|
||||||
|
|
||||||
|
destroy$ = inject(DestroyRef);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly logger: NGXLogger,
|
readonly logger: NGXLogger,
|
||||||
readonly assessmentsProvider: AssessmentsProvider,
|
readonly assessmentsProvider: AssessmentsProvider,
|
||||||
@@ -62,18 +62,17 @@ export class AssessmentsPageComponent implements OnInit, AfterViewInit, OnDestro
|
|||||||
this.segmentView.value = this.sharedAxisChoreographer.currentValue;
|
this.segmentView.value = this.sharedAxisChoreographer.currentValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
this.routingSubscription.unsubscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.routingSubscription = this.dataRoutingService.itemSelectListener().subscribe(thing => {
|
this.dataRoutingService
|
||||||
void this.router.navigate(['assessments', 'detail', thing.uid], {
|
.itemSelectListener()
|
||||||
queryParams: {
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
token: this.activatedRoute.snapshot.queryParamMap.get('token'),
|
.subscribe(thing => {
|
||||||
},
|
void this.router.navigate(['assessments', 'detail', thing.uid], {
|
||||||
|
queryParams: {
|
||||||
|
token: this.activatedRoute.snapshot.queryParamMap.get('token'),
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
this.activatedRoute.queryParams.subscribe(parameters => {
|
this.activatedRoute.queryParams.subscribe(parameters => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 StApps
|
* Copyright (C) 2023 StApps
|
||||||
* This program is free software: you can redistribute it and/or modify it
|
* 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
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
* Software Foundation, version 3.
|
* Software Foundation, version 3.
|
||||||
@@ -12,41 +12,29 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* 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 {OnInit, OnDestroy, Component} from '@angular/core';
|
|
||||||
import {NavController} from '@ionic/angular';
|
import {NavController} from '@ionic/angular';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
import {AuthActions, IAuthAction} from 'ionic-appauth';
|
import {AuthActions, IAuthAction} from 'ionic-appauth';
|
||||||
import {Subscription} from 'rxjs';
|
|
||||||
import {SCAuthorizationProviderType} from '@openstapps/core';
|
import {SCAuthorizationProviderType} from '@openstapps/core';
|
||||||
import {AuthHelperService} from '../../auth-helper.service';
|
import {AuthHelperService} from '../../auth-helper.service';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
import {Observable} from 'rxjs';
|
||||||
|
import {IPAIAAuthAction} from '../../paia/paia-auth-action';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: 'auth-callback-page.component.html',
|
templateUrl: 'auth-callback-page.component.html',
|
||||||
styleUrls: ['auth-callback-page.component.scss'],
|
styleUrls: ['auth-callback-page.component.scss'],
|
||||||
})
|
})
|
||||||
export class AuthCallbackPageComponent implements OnInit, OnDestroy {
|
export class AuthCallbackPageComponent {
|
||||||
PROVIDER_TYPE: SCAuthorizationProviderType = 'default';
|
PROVIDER_TYPE: SCAuthorizationProviderType = 'default';
|
||||||
|
|
||||||
private authEvents: Subscription;
|
constructor(private navCtrl: NavController, private router: Router, private authHelper: AuthHelperService) {
|
||||||
|
const provider = this.authHelper.getProvider(this.PROVIDER_TYPE);
|
||||||
|
const events: Observable<IPAIAAuthAction | IAuthAction> = provider.events$;
|
||||||
|
|
||||||
constructor(
|
events.pipe(takeUntilDestroyed()).subscribe((action: IAuthAction) => this.postCallback(action));
|
||||||
private navCtrl: NavController,
|
provider.authorizationCallback(window.location.origin + this.router.url);
|
||||||
private router: Router,
|
|
||||||
private authHelper: AuthHelperService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this.authEvents = this.authHelper
|
|
||||||
.getProvider(this.PROVIDER_TYPE)
|
|
||||||
.events$.subscribe((action: IAuthAction) => this.postCallback(action));
|
|
||||||
this.authHelper
|
|
||||||
.getProvider(this.PROVIDER_TYPE)
|
|
||||||
.authorizationCallback(window.location.origin + this.router.url);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
this.authEvents.unsubscribe();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async postCallback(action: IAuthAction) {
|
async postCallback(action: IAuthAction) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 StApps
|
* Copyright (C) 2023 StApps
|
||||||
* This program is free software: you can redistribute it and/or modify it
|
* 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
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
* Software Foundation, version 3.
|
* Software Foundation, version 3.
|
||||||
@@ -12,8 +12,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
import {DestroyRef, inject, Injectable} from '@angular/core';
|
||||||
import {Injectable, OnDestroy} from '@angular/core';
|
|
||||||
import {
|
import {
|
||||||
DateSeriesRelevantData,
|
DateSeriesRelevantData,
|
||||||
dateSeriesRelevantKeys,
|
dateSeriesRelevantKeys,
|
||||||
@@ -28,7 +27,6 @@ import {BackgroundFetch} from '@transistorsoft/capacitor-background-fetch';
|
|||||||
import {StorageProvider} from '../../storage/storage.provider';
|
import {StorageProvider} from '../../storage/storage.provider';
|
||||||
import {CalendarService} from '../../calendar/calendar.service';
|
import {CalendarService} from '../../calendar/calendar.service';
|
||||||
import {toICal} from '../../calendar/ical/ical';
|
import {toICal} from '../../calendar/ical/ical';
|
||||||
import {Subscription} from 'rxjs';
|
|
||||||
import {ChangesOf} from './changes';
|
import {ChangesOf} from './changes';
|
||||||
import {hashStringToInt} from './hash';
|
import {hashStringToInt} from './hash';
|
||||||
import {
|
import {
|
||||||
@@ -38,9 +36,12 @@ import {
|
|||||||
} from '../../settings/page/calendar-sync-settings-keys';
|
} from '../../settings/page/calendar-sync-settings-keys';
|
||||||
import {filter} from 'rxjs/operators';
|
import {filter} from 'rxjs/operators';
|
||||||
import {Capacitor} from '@capacitor/core';
|
import {Capacitor} from '@capacitor/core';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ScheduleSyncService implements OnDestroy {
|
export class ScheduleSyncService {
|
||||||
|
destroy$ = inject(DestroyRef);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private scheduleProvider: ScheduleProvider,
|
private scheduleProvider: ScheduleProvider,
|
||||||
private storageProvider: StorageProvider,
|
private storageProvider: StorageProvider,
|
||||||
@@ -51,20 +52,19 @@ export class ScheduleSyncService implements OnDestroy {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.scheduleProvider.uuids$.pipe(filter(uuids => uuids?.length > 0)).subscribe(uuids => {
|
this.scheduleProvider.uuids$
|
||||||
this.uuids = uuids;
|
.pipe(
|
||||||
void this.syncNativeCalendar();
|
takeUntilDestroyed(this.destroy$),
|
||||||
});
|
filter(uuids => uuids?.length > 0),
|
||||||
|
)
|
||||||
|
.subscribe(uuids => {
|
||||||
|
this.uuids = uuids;
|
||||||
|
void this.syncNativeCalendar();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
uuids: SCUuid[];
|
uuids: SCUuid[];
|
||||||
|
|
||||||
uuidSubscription: Subscription;
|
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
this.uuidSubscription?.unsubscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async isSyncEnabled(): Promise<boolean> {
|
private async isSyncEnabled(): Promise<boolean> {
|
||||||
return getCalendarSetting(this.storageProvider, CALENDAR_SYNC_ENABLED_KEY);
|
return getCalendarSetting(this.storageProvider, CALENDAR_SYNC_ENABLED_KEY);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
/* eslint-disable unicorn/no-null */
|
/* eslint-disable unicorn/no-null */
|
||||||
import {Injectable, OnDestroy} from '@angular/core';
|
import {DestroyRef, inject, Injectable, OnDestroy} from '@angular/core';
|
||||||
import {
|
import {
|
||||||
Bounds,
|
Bounds,
|
||||||
SCDateSeries,
|
SCDateSeries,
|
||||||
@@ -23,11 +23,12 @@ import {
|
|||||||
SCThingType,
|
SCThingType,
|
||||||
SCUuid,
|
SCUuid,
|
||||||
} from '@openstapps/core';
|
} from '@openstapps/core';
|
||||||
import {BehaviorSubject, Observable, Subscription} from 'rxjs';
|
import {BehaviorSubject, Observable} from 'rxjs';
|
||||||
import {DataProvider} from '../data/data.provider';
|
import {DataProvider} from '../data/data.provider';
|
||||||
import {map} from 'rxjs/operators';
|
import {map} from 'rxjs/operators';
|
||||||
import {DateFormatPipe, DurationPipe} from 'ngx-moment';
|
import {DateFormatPipe, DurationPipe} from 'ngx-moment';
|
||||||
import {pick} from '@openstapps/collection-utils';
|
import {pick} from '@openstapps/collection-utils';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -73,7 +74,7 @@ export class ScheduleProvider implements OnDestroy {
|
|||||||
|
|
||||||
private _partialEvents$?: BehaviorSubject<DateSeriesRelevantData[]>;
|
private _partialEvents$?: BehaviorSubject<DateSeriesRelevantData[]>;
|
||||||
|
|
||||||
private _partialEventsSubscription?: Subscription;
|
destroy$ = inject(DestroyRef);
|
||||||
|
|
||||||
constructor(private readonly dataProvider: DataProvider) {
|
constructor(private readonly dataProvider: DataProvider) {
|
||||||
window.addEventListener('storage', this.storageListener);
|
window.addEventListener('storage', this.storageListener);
|
||||||
@@ -125,7 +126,7 @@ export class ScheduleProvider implements OnDestroy {
|
|||||||
const data = ScheduleProvider.get<DateSeriesRelevantData>(ScheduleProvider.partialEventsStorageKey);
|
const data = ScheduleProvider.get<DateSeriesRelevantData>(ScheduleProvider.partialEventsStorageKey);
|
||||||
|
|
||||||
this._partialEvents$ = new BehaviorSubject(data ?? []);
|
this._partialEvents$ = new BehaviorSubject(data ?? []);
|
||||||
this._partialEventsSubscription = this._partialEvents$.subscribe(result => {
|
this._partialEvents$.pipe(takeUntilDestroyed(this.destroy$)).subscribe(result => {
|
||||||
ScheduleProvider.set(ScheduleProvider.partialEventsStorageKey, result);
|
ScheduleProvider.set(ScheduleProvider.partialEventsStorageKey, result);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -257,7 +258,6 @@ export class ScheduleProvider implements OnDestroy {
|
|||||||
* TODO
|
* TODO
|
||||||
*/
|
*/
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this._partialEventsSubscription?.unsubscribe();
|
|
||||||
window.removeEventListener('storage', this.storageListener);
|
window.removeEventListener('storage', this.storageListener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,22 +12,22 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component, OnInit, OnDestroy} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {Router, ActivatedRoute} from '@angular/router';
|
import {Router, ActivatedRoute} from '@angular/router';
|
||||||
import {SCCatalog, SCSemester} from '@openstapps/core';
|
import {SCCatalog, SCSemester} from '@openstapps/core';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {Subscription} from 'rxjs';
|
|
||||||
import {CatalogProvider} from './catalog.provider';
|
import {CatalogProvider} from './catalog.provider';
|
||||||
import {NGXLogger} from 'ngx-logger';
|
import {NGXLogger} from 'ngx-logger';
|
||||||
import {Location} from '@angular/common';
|
import {Location} from '@angular/common';
|
||||||
import {DataRoutingService} from '../data/data-routing.service';
|
import {DataRoutingService} from '../data/data-routing.service';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-catalog',
|
selector: 'app-catalog',
|
||||||
templateUrl: './catalog.component.html',
|
templateUrl: './catalog.component.html',
|
||||||
styleUrls: ['./catalog.component.scss'],
|
styleUrls: ['./catalog.component.scss'],
|
||||||
})
|
})
|
||||||
export class CatalogComponent implements OnInit, OnDestroy {
|
export class CatalogComponent implements OnInit {
|
||||||
/**
|
/**
|
||||||
* SCSemester to show
|
* SCSemester to show
|
||||||
*/
|
*/
|
||||||
@@ -48,11 +48,6 @@ export class CatalogComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
catalogs: SCCatalog[] | undefined;
|
catalogs: SCCatalog[] | undefined;
|
||||||
|
|
||||||
/**
|
|
||||||
* Array of all subscriptions to Observables
|
|
||||||
*/
|
|
||||||
subscriptions: Subscription[] = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Supercatalog (SCCatalog) to refer to
|
* Supercatalog (SCCatalog) to refer to
|
||||||
*/
|
*/
|
||||||
@@ -66,11 +61,12 @@ export class CatalogComponent implements OnInit, OnDestroy {
|
|||||||
protected router: Router,
|
protected router: Router,
|
||||||
public location: Location,
|
public location: Location,
|
||||||
) {
|
) {
|
||||||
this.subscriptions.push(
|
this.dataRoutingService
|
||||||
this.dataRoutingService.itemSelectListener().subscribe(item => {
|
.itemSelectListener()
|
||||||
|
.pipe(takeUntilDestroyed())
|
||||||
|
.subscribe(item => {
|
||||||
void this.router.navigate(['data-detail', item.uid]);
|
void this.router.navigate(['data-detail', item.uid]);
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@@ -78,15 +74,6 @@ export class CatalogComponent implements OnInit, OnDestroy {
|
|||||||
void this.fetchCatalog();
|
void this.fetchCatalog();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove subscriptions when the component is removed
|
|
||||||
*/
|
|
||||||
ngOnDestroy() {
|
|
||||||
for (const sub of this.subscriptions) {
|
|
||||||
sub.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchCatalog() {
|
async fetchCatalog() {
|
||||||
try {
|
try {
|
||||||
if (this.availableSemesters.length === 0) {
|
if (this.availableSemesters.length === 0) {
|
||||||
|
|||||||
@@ -12,21 +12,18 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
import {Component, DestroyRef, ElementRef, inject, NgZone, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
import {Location} from '@angular/common';
|
import {Location} from '@angular/common';
|
||||||
import {Subscription} from 'rxjs';
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {SCDateSeries, SCUuid} from '@openstapps/core';
|
import {SCDateSeries, SCUuid} from '@openstapps/core';
|
||||||
import {SplashScreen} from '@capacitor/splash-screen';
|
import {SplashScreen} from '@capacitor/splash-screen';
|
||||||
|
|
||||||
import {DataRoutingService} from '../data/data-routing.service';
|
import {DataRoutingService} from '../data/data-routing.service';
|
||||||
import {ScheduleProvider} from '../calendar/schedule.provider';
|
import {ScheduleProvider} from '../calendar/schedule.provider';
|
||||||
import {AnimationController, IonContent} from '@ionic/angular';
|
import {AnimationController, IonContent} from '@ionic/angular';
|
||||||
import {DashboardCollapse} from './dashboard-collapse';
|
import {DashboardCollapse} from './dashboard-collapse';
|
||||||
import {BreakpointObserver} from '@angular/cdk/layout';
|
import {BreakpointObserver} from '@angular/cdk/layout';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
// const scrollTimeline = new ScrollTimeline();
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-dashboard',
|
selector: 'app-dashboard',
|
||||||
@@ -34,11 +31,6 @@ import {BreakpointObserver} from '@angular/cdk/layout';
|
|||||||
styleUrls: ['./dashboard.component.scss', '/dashboard.collapse.component.scss'],
|
styleUrls: ['./dashboard.component.scss', '/dashboard.collapse.component.scss'],
|
||||||
})
|
})
|
||||||
export class DashboardComponent implements OnInit, OnDestroy {
|
export class DashboardComponent implements OnInit, OnDestroy {
|
||||||
/**
|
|
||||||
* Array of all subscriptions to Observables
|
|
||||||
*/
|
|
||||||
subscriptions: Subscription[] = [];
|
|
||||||
|
|
||||||
@ViewChild('toolbar', {read: ElementRef}) toolbarRef: ElementRef;
|
@ViewChild('toolbar', {read: ElementRef}) toolbarRef: ElementRef;
|
||||||
|
|
||||||
@ViewChild('schedule', {read: ElementRef}) scheduleRef: ElementRef;
|
@ViewChild('schedule', {read: ElementRef}) scheduleRef: ElementRef;
|
||||||
@@ -47,11 +39,6 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
collapseAnimation: DashboardCollapse;
|
collapseAnimation: DashboardCollapse;
|
||||||
|
|
||||||
/**
|
|
||||||
* UUID subscription
|
|
||||||
*/
|
|
||||||
private _eventUuidSubscription: Subscription;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The events to display
|
* The events to display
|
||||||
*/
|
*/
|
||||||
@@ -74,6 +61,8 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
destroy$ = inject(DestroyRef);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly dataRoutingService: DataRoutingService,
|
private readonly dataRoutingService: DataRoutingService,
|
||||||
private scheduleProvider: ScheduleProvider,
|
private scheduleProvider: ScheduleProvider,
|
||||||
@@ -83,15 +72,16 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
|||||||
private breakpointObserver: BreakpointObserver,
|
private breakpointObserver: BreakpointObserver,
|
||||||
private zone: NgZone,
|
private zone: NgZone,
|
||||||
) {
|
) {
|
||||||
this.subscriptions.push(
|
this.dataRoutingService
|
||||||
this.dataRoutingService.itemSelectListener().subscribe(item => {
|
.itemSelectListener()
|
||||||
|
.pipe(takeUntilDestroyed())
|
||||||
|
.subscribe(item => {
|
||||||
void this.router.navigate(['data-detail', item.uid]);
|
void this.router.navigate(['data-detail', item.uid]);
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this._eventUuidSubscription = this.scheduleProvider.uuids$.subscribe(async result => {
|
this.scheduleProvider.uuids$.pipe(takeUntilDestroyed(this.destroy$)).subscribe(async result => {
|
||||||
this.eventUuids = result;
|
this.eventUuids = result;
|
||||||
await this.loadNextEvent();
|
await this.loadNextEvent();
|
||||||
});
|
});
|
||||||
@@ -105,12 +95,13 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
|||||||
this.scheduleRef.nativeElement,
|
this.scheduleRef.nativeElement,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.subscriptions.push(
|
this.breakpointObserver
|
||||||
this.breakpointObserver.observe(['(min-width: 768px)']).subscribe(async state => {
|
.observe(['(min-width: 768px)'])
|
||||||
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
|
.subscribe(async state => {
|
||||||
await this.collapseAnimation.ready;
|
await this.collapseAnimation.ready;
|
||||||
this.collapseAnimation.active = !state.matches;
|
this.collapseAnimation.active = !state.matches;
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadNextEvent() {
|
async loadNextEvent() {
|
||||||
@@ -133,14 +124,7 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
|||||||
.find(({time}) => !!time)?.series;
|
.find(({time}) => !!time)?.series;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove subscriptions when the component is removed
|
|
||||||
*/
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
for (const sub of this.subscriptions) {
|
|
||||||
sub.unsubscribe();
|
|
||||||
}
|
|
||||||
this._eventUuidSubscription.unsubscribe();
|
|
||||||
this.collapseAnimation.destroy();
|
this.collapseAnimation.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 StApps
|
* Copyright (C) 2023 StApps
|
||||||
* This program is free software: you can redistribute it and/or modify it
|
* 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
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
* Software Foundation, version 3.
|
* Software Foundation, version 3.
|
||||||
@@ -12,9 +12,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
import {Component, DestroyRef, inject, Input, ViewChild} from '@angular/core';
|
||||||
/* tslint:disable:prefer-function-over-method */
|
|
||||||
import {Component, Input, OnDestroy, ViewChild} from '@angular/core';
|
|
||||||
import {IonRouterOutlet, ModalController} from '@ionic/angular';
|
import {IonRouterOutlet, ModalController} from '@ionic/angular';
|
||||||
import {SCDateSeries, SCThing, SCThingType, SCUuid} from '@openstapps/core';
|
import {SCDateSeries, SCThing, SCThingType, SCUuid} from '@openstapps/core';
|
||||||
import {Subscription} from 'rxjs';
|
import {Subscription} from 'rxjs';
|
||||||
@@ -27,6 +25,7 @@ import {
|
|||||||
import {AddEventStates, AddEventStatesMap} from './add-event-action-chip.config';
|
import {AddEventStates, AddEventStatesMap} from './add-event-action-chip.config';
|
||||||
import {EditEventSelectionComponent} from '../edit-event-selection.component';
|
import {EditEventSelectionComponent} from '../edit-event-selection.component';
|
||||||
import {AddEventReviewModalComponent} from '../../../calendar/add-event-review-modal.component';
|
import {AddEventReviewModalComponent} from '../../../calendar/add-event-review-modal.component';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows a horizontal list of action chips
|
* Shows a horizontal list of action chips
|
||||||
@@ -37,7 +36,7 @@ import {AddEventReviewModalComponent} from '../../../calendar/add-event-review-m
|
|||||||
styleUrls: ['add-event-action-chip.scss'],
|
styleUrls: ['add-event-action-chip.scss'],
|
||||||
animations: [chipSkeletonTransition, chipTransition],
|
animations: [chipSkeletonTransition, chipTransition],
|
||||||
})
|
})
|
||||||
export class AddEventActionChipComponent implements OnDestroy {
|
export class AddEventActionChipComponent {
|
||||||
/**
|
/**
|
||||||
* Associated date series
|
* Associated date series
|
||||||
*/
|
*/
|
||||||
@@ -91,6 +90,8 @@ export class AddEventActionChipComponent implements OnDestroy {
|
|||||||
@ViewChild('selection', {static: false})
|
@ViewChild('selection', {static: false})
|
||||||
selection: EditEventSelectionComponent;
|
selection: EditEventSelectionComponent;
|
||||||
|
|
||||||
|
destroy$ = inject(DestroyRef);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly dataProvider: CoordinatedSearchProvider,
|
readonly dataProvider: CoordinatedSearchProvider,
|
||||||
readonly modalController: ModalController,
|
readonly modalController: ModalController,
|
||||||
@@ -111,13 +112,6 @@ export class AddEventActionChipComponent implements OnDestroy {
|
|||||||
this.color = color;
|
this.color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
ngOnDestroy() {
|
|
||||||
this.uuidSubscription?.unsubscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
async export() {
|
async export() {
|
||||||
const modal = await this.modalController.create({
|
const modal = await this.modalController.create({
|
||||||
component: AddEventReviewModalComponent,
|
component: AddEventReviewModalComponent,
|
||||||
@@ -180,28 +174,30 @@ export class AddEventActionChipComponent implements OnDestroy {
|
|||||||
})
|
})
|
||||||
.then(it => it.data as SCDateSeries[]);
|
.then(it => it.data as SCDateSeries[]);
|
||||||
|
|
||||||
this.uuidSubscription = this.scheduleProvider.uuids$.subscribe(async result => {
|
this.uuidSubscription = this.scheduleProvider.uuids$
|
||||||
this.uuids = result;
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
const associatedDateSeries = await this.associatedDateSeries;
|
.subscribe(async result => {
|
||||||
if (associatedDateSeries.length === 0) {
|
this.uuids = result;
|
||||||
this.applyState(AddEventStates.UNAVAILABLE);
|
const associatedDateSeries = await this.associatedDateSeries;
|
||||||
|
if (associatedDateSeries.length === 0) {
|
||||||
|
this.applyState(AddEventStates.UNAVAILABLE);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
switch (associatedDateSeries.map(it => it.uid).filter(it => !this.uuids.includes(it)).length) {
|
|
||||||
case 0: {
|
|
||||||
this.applyState(AddEventStates.ADDED_ALL);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case associatedDateSeries.length: {
|
switch (associatedDateSeries.map(it => it.uid).filter(it => !this.uuids.includes(it)).length) {
|
||||||
this.applyState(AddEventStates.REMOVED_ALL);
|
case 0: {
|
||||||
break;
|
this.applyState(AddEventStates.ADDED_ALL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case associatedDateSeries.length: {
|
||||||
|
this.applyState(AddEventStates.REMOVED_ALL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
this.applyState(AddEventStates.ADDED_SOME);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
default: {
|
});
|
||||||
this.applyState(AddEventStates.ADDED_SOME);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,27 +12,26 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
|
import {Component, DestroyRef, inject, Input, OnInit} from '@angular/core';
|
||||||
import {RoutingStackService} from '../../../util/routing-stack.service';
|
import {RoutingStackService} from '../../../util/routing-stack.service';
|
||||||
import {SCCatalog, SCThings, SCThingType, SCThingWithoutReferences} from '@openstapps/core';
|
import {SCCatalog, SCThings, SCThingType, SCThingWithoutReferences} from '@openstapps/core';
|
||||||
import {DataProvider, DataScope} from '../data.provider';
|
import {DataProvider, DataScope} from '../data.provider';
|
||||||
import {fromEvent, Observable, Subscription} from 'rxjs';
|
import {fromEvent, Observable} from 'rxjs';
|
||||||
import {map, startWith} from 'rxjs/operators';
|
import {map, startWith} from 'rxjs/operators';
|
||||||
import {DataRoutingService} from '../data-routing.service';
|
import {DataRoutingService} from '../data-routing.service';
|
||||||
import {NavController} from '@ionic/angular';
|
import {NavController} from '@ionic/angular';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'stapps-data-path',
|
selector: 'stapps-data-path',
|
||||||
templateUrl: './data-path.html',
|
templateUrl: './data-path.html',
|
||||||
styleUrls: ['./data-path.scss'],
|
styleUrls: ['./data-path.scss'],
|
||||||
})
|
})
|
||||||
export class DataPathComponent implements OnInit, OnDestroy {
|
export class DataPathComponent implements OnInit {
|
||||||
path: Promise<SCThingWithoutReferences[]>;
|
path: Promise<SCThingWithoutReferences[]>;
|
||||||
|
|
||||||
$width: Observable<number>;
|
$width: Observable<number>;
|
||||||
|
|
||||||
subscriptions: Subscription[] = [];
|
|
||||||
|
|
||||||
@Input() autoRouting = true;
|
@Input() autoRouting = true;
|
||||||
|
|
||||||
@Input() maxItems = 2;
|
@Input() maxItems = 2;
|
||||||
@@ -74,6 +73,8 @@ export class DataPathComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
destroy$ = inject(DestroyRef);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly dataRoutingService: DataRoutingService,
|
readonly dataRoutingService: DataRoutingService,
|
||||||
readonly navController: NavController,
|
readonly navController: NavController,
|
||||||
@@ -88,14 +89,11 @@ export class DataPathComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!this.autoRouting) return;
|
if (!this.autoRouting) return;
|
||||||
this.subscriptions.push(
|
this.dataRoutingService
|
||||||
this.dataRoutingService.pathSelectListener().subscribe(item => {
|
.pathSelectListener()
|
||||||
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
|
.subscribe(item => {
|
||||||
void this.navController.navigateBack(['data-detail', item.uid]);
|
void this.navController.navigateBack(['data-detail', item.uid]);
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
for (const sub of this.subscriptions) sub.unsubscribe();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,11 +15,12 @@
|
|||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
ContentChild,
|
ContentChild,
|
||||||
|
DestroyRef,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
HostListener,
|
HostListener,
|
||||||
|
inject,
|
||||||
Input,
|
Input,
|
||||||
OnChanges,
|
OnChanges,
|
||||||
OnDestroy,
|
|
||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
SimpleChanges,
|
SimpleChanges,
|
||||||
@@ -27,8 +28,9 @@ import {
|
|||||||
ViewChild,
|
ViewChild,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import {SCThings} from '@openstapps/core';
|
import {SCThings} from '@openstapps/core';
|
||||||
import {BehaviorSubject, Observable, Subscription} from 'rxjs';
|
import {BehaviorSubject, Observable} from 'rxjs';
|
||||||
import {IonInfiniteScroll} from '@ionic/angular';
|
import {IonInfiniteScroll} from '@ionic/angular';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
export interface DataListContext<T> {
|
export interface DataListContext<T> {
|
||||||
$implicit: T;
|
$implicit: T;
|
||||||
@@ -42,7 +44,7 @@ export interface DataListContext<T> {
|
|||||||
templateUrl: 'data-list.html',
|
templateUrl: 'data-list.html',
|
||||||
styleUrls: ['data-list.scss'],
|
styleUrls: ['data-list.scss'],
|
||||||
})
|
})
|
||||||
export class DataListComponent implements OnChanges, OnInit, OnDestroy {
|
export class DataListComponent implements OnChanges, OnInit {
|
||||||
/**
|
/**
|
||||||
* All SCThings to display
|
* All SCThings to display
|
||||||
*/
|
*/
|
||||||
@@ -76,11 +78,6 @@ export class DataListComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
skeletonItems: number;
|
skeletonItems: number;
|
||||||
|
|
||||||
/**
|
|
||||||
* Array of all subscriptions to Observables
|
|
||||||
*/
|
|
||||||
subscriptions: Subscription[] = [];
|
|
||||||
|
|
||||||
@ViewChild(IonInfiniteScroll) infiniteScroll: IonInfiniteScroll;
|
@ViewChild(IonInfiniteScroll) infiniteScroll: IonInfiniteScroll;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -88,6 +85,8 @@ export class DataListComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
@Input() loading = true;
|
@Input() loading = true;
|
||||||
|
|
||||||
|
destroy$ = inject(DestroyRef);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate how many items would fill the screen
|
* Calculate how many items would fill the screen
|
||||||
*/
|
*/
|
||||||
@@ -112,20 +111,12 @@ export class DataListComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
for (const subscription of this.subscriptions) {
|
|
||||||
subscription.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.calcSkeletonItems();
|
this.calcSkeletonItems();
|
||||||
if (this.resetToTop !== undefined) {
|
if (this.resetToTop !== undefined) {
|
||||||
this.subscriptions.push(
|
this.resetToTop.pipe(takeUntilDestroyed(this.destroy$)).subscribe(() => {
|
||||||
this.resetToTop.subscribe(() => {
|
// this.viewPort.scrollToIndex(0);
|
||||||
// this.viewPort.scrollToIndex(0);
|
});
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,11 +12,13 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {MapPosition} from '../../map/position.service';
|
import {MapPosition} from '../../map/position.service';
|
||||||
import {SearchPageComponent} from './search-page.component';
|
import {SearchPageComponent} from './search-page.component';
|
||||||
import {Geolocation} from '@capacitor/geolocation';
|
import {Geolocation} from '@capacitor/geolocation';
|
||||||
import {Subscription} from 'rxjs';
|
import {BehaviorSubject} from 'rxjs';
|
||||||
|
import {pauseWhen} from '../../../util/pause-when';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Presents a list of places for eating/drinking
|
* Presents a list of places for eating/drinking
|
||||||
@@ -25,19 +27,29 @@ import {Subscription} from 'rxjs';
|
|||||||
templateUrl: 'search-page.html',
|
templateUrl: 'search-page.html',
|
||||||
styleUrls: ['../../data/list/search-page.scss'],
|
styleUrls: ['../../data/list/search-page.scss'],
|
||||||
})
|
})
|
||||||
export class FoodDataListComponent extends SearchPageComponent implements OnInit, OnDestroy {
|
export class FoodDataListComponent extends SearchPageComponent implements OnInit {
|
||||||
title = 'canteens.title';
|
title = 'canteens.title';
|
||||||
|
|
||||||
showNavigation = false;
|
showNavigation = false;
|
||||||
|
|
||||||
locationWatch?: Subscription;
|
isNotInView$ = new BehaviorSubject(true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the forced filter to present only places for eating/drinking
|
* Sets the forced filter to present only places for eating/drinking
|
||||||
*/
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.locationWatch?.unsubscribe();
|
this.positionService
|
||||||
this.locationWatch = this.createLocationWatch();
|
.watchCurrentLocation({enableHighAccuracy: false, maximumAge: 1000})
|
||||||
|
.pipe(pauseWhen(this.isNotInView$), takeUntilDestroyed(this.destroy$))
|
||||||
|
.subscribe({
|
||||||
|
next: (position: MapPosition) => {
|
||||||
|
this.positionService.position = position;
|
||||||
|
},
|
||||||
|
error: async _error => {
|
||||||
|
this.positionService.position = undefined;
|
||||||
|
await Geolocation.checkPermissions();
|
||||||
|
},
|
||||||
|
});
|
||||||
this.showDefaultData = true;
|
this.showDefaultData = true;
|
||||||
|
|
||||||
this.sortQuery = [
|
this.sortQuery = [
|
||||||
@@ -101,32 +113,12 @@ export class FoodDataListComponent extends SearchPageComponent implements OnInit
|
|||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private createLocationWatch(): Subscription {
|
|
||||||
return 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();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async ionViewWillEnter() {
|
async ionViewWillEnter() {
|
||||||
await super.ionViewWillEnter();
|
await super.ionViewWillEnter();
|
||||||
this.locationWatch?.unsubscribe();
|
this.isNotInView$.next(false);
|
||||||
this.locationWatch = this.createLocationWatch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ionViewWillLeave() {
|
ionViewWillLeave() {
|
||||||
this.locationWatch?.unsubscribe();
|
this.isNotInView$.next(true);
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
super.ngOnDestroy();
|
|
||||||
this.locationWatch?.unsubscribe();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
|
import {Component, DestroyRef, inject, Input, OnInit} from '@angular/core';
|
||||||
import {ActivatedRoute, Router} from '@angular/router';
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
import {Keyboard} from '@capacitor/keyboard';
|
import {Keyboard} from '@capacitor/keyboard';
|
||||||
import {AlertController, AnimationBuilder, AnimationController} from '@ionic/angular';
|
import {AlertController, AnimationBuilder, AnimationController} from '@ionic/angular';
|
||||||
@@ -26,7 +26,7 @@ import {
|
|||||||
SCThings,
|
SCThings,
|
||||||
} from '@openstapps/core';
|
} from '@openstapps/core';
|
||||||
import {NGXLogger} from 'ngx-logger';
|
import {NGXLogger} from 'ngx-logger';
|
||||||
import {combineLatest, Subject, Subscription} from 'rxjs';
|
import {combineLatest, Subject} from 'rxjs';
|
||||||
import {debounceTime, distinctUntilChanged, startWith} from 'rxjs/operators';
|
import {debounceTime, distinctUntilChanged, startWith} from 'rxjs/operators';
|
||||||
import {ContextMenuService} from '../../menu/context/context-menu.service';
|
import {ContextMenuService} from '../../menu/context/context-menu.service';
|
||||||
import {SettingsProvider} from '../../settings/settings.provider';
|
import {SettingsProvider} from '../../settings/settings.provider';
|
||||||
@@ -35,6 +35,7 @@ import {DataProvider} from '../data.provider';
|
|||||||
import {PositionService} from '../../map/position.service';
|
import {PositionService} from '../../map/position.service';
|
||||||
import {ConfigProvider} from '../../config/config.provider';
|
import {ConfigProvider} from '../../config/config.provider';
|
||||||
import {searchPageSwitchAnimation} from './search-page-switch-animation';
|
import {searchPageSwitchAnimation} from './search-page-switch-animation';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SearchPageComponent queries things and shows list of things as search results and filter as context menu
|
* SearchPageComponent queries things and shows list of things as search results and filter as context menu
|
||||||
@@ -45,7 +46,7 @@ import {searchPageSwitchAnimation} from './search-page-switch-animation';
|
|||||||
styleUrls: ['search-page.scss'],
|
styleUrls: ['search-page.scss'],
|
||||||
providers: [ContextMenuService],
|
providers: [ContextMenuService],
|
||||||
})
|
})
|
||||||
export class SearchPageComponent implements OnInit, OnDestroy {
|
export class SearchPageComponent implements OnInit {
|
||||||
@Input() title = 'search.title';
|
@Input() title = 'search.title';
|
||||||
|
|
||||||
@Input() placeholder = 'search.search_bar.placeholder';
|
@Input() placeholder = 'search.search_bar.placeholder';
|
||||||
@@ -141,10 +142,7 @@ export class SearchPageComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
sortQuery: SCSearchSort[] | undefined;
|
sortQuery: SCSearchSort[] | undefined;
|
||||||
|
|
||||||
/**
|
destroy$ = inject(DestroyRef);
|
||||||
* Array of all subscriptions to Observables
|
|
||||||
*/
|
|
||||||
subscriptions: Subscription[] = [];
|
|
||||||
|
|
||||||
routeAnimation: AnimationBuilder;
|
routeAnimation: AnimationBuilder;
|
||||||
|
|
||||||
@@ -286,7 +284,7 @@ export class SearchPageComponent implements OnInit, OnDestroy {
|
|||||||
this.contextMenuService.updateContextFilter(facets);
|
this.contextMenuService.updateContextFilter(facets);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit(defaultListeners = true) {
|
||||||
this.initialize();
|
this.initialize();
|
||||||
this.contextMenuService.setContextSort({
|
this.contextMenuService.setContextSort({
|
||||||
name: 'sort',
|
name: 'sort',
|
||||||
@@ -308,7 +306,7 @@ export class SearchPageComponent implements OnInit, OnDestroy {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
this.subscriptions.push(
|
if (defaultListeners) {
|
||||||
combineLatest([
|
combineLatest([
|
||||||
this.queryTextChanged.pipe(
|
this.queryTextChanged.pipe(
|
||||||
debounceTime(this.searchQueryDueTime),
|
debounceTime(this.searchQueryDueTime),
|
||||||
@@ -317,30 +315,37 @@ export class SearchPageComponent implements OnInit, OnDestroy {
|
|||||||
),
|
),
|
||||||
this.contextMenuService.filterQueryChanged$.pipe(startWith(this.filterQuery)),
|
this.contextMenuService.filterQueryChanged$.pipe(startWith(this.filterQuery)),
|
||||||
this.contextMenuService.sortQueryChanged$.pipe(startWith(this.sortQuery)),
|
this.contextMenuService.sortQueryChanged$.pipe(startWith(this.sortQuery)),
|
||||||
]).subscribe(async query => {
|
])
|
||||||
this.queryText = query[0];
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
this.filterQuery = query[1];
|
.subscribe(async query => {
|
||||||
this.sortQuery = query[2];
|
this.queryText = query[0];
|
||||||
this.from = 0;
|
this.filterQuery = query[1];
|
||||||
if (this.filterQuery !== undefined || this.queryText?.length > 0 || this.showDefaultData) {
|
this.sortQuery = query[2];
|
||||||
await this.fetchAndUpdateItems();
|
this.from = 0;
|
||||||
this.queryChanged.next();
|
if (this.filterQuery !== undefined || this.queryText?.length > 0 || this.showDefaultData) {
|
||||||
}
|
await this.fetchAndUpdateItems();
|
||||||
}),
|
this.queryChanged.next();
|
||||||
this.settingsProvider.settingsActionChanged$.subscribe(({type, payload}) => {
|
}
|
||||||
if (type === 'stapps.settings.changed') {
|
});
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
this.settingsProvider.settingsActionChanged$
|
||||||
const {category, name, value} = payload!;
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
this.logger.log(`received event "settings.changed" with category:
|
.subscribe(({type, payload}) => {
|
||||||
|
if (type === 'stapps.settings.changed') {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
const {category, name, value} = payload!;
|
||||||
|
this.logger.log(`received event "settings.changed" with category:
|
||||||
${category}, name: ${name}, value: ${JSON.stringify(value)}`);
|
${category}, name: ${name}, value: ${JSON.stringify(value)}`);
|
||||||
}
|
}
|
||||||
}),
|
});
|
||||||
this.dataRoutingService.itemSelectListener().subscribe(item => {
|
this.dataRoutingService
|
||||||
if (this.itemRouting) {
|
.itemSelectListener()
|
||||||
void this.router.navigate(['/data-detail', item.uid]);
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
}
|
.subscribe(item => {
|
||||||
}),
|
if (this.itemRouting) {
|
||||||
);
|
void this.router.navigate(['/data-detail', item.uid]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const features = this.configProvider.getValue('features') as SCFeatureConfiguration;
|
const features = this.configProvider.getValue('features') as SCFeatureConfiguration;
|
||||||
this.isHebisAvailable = !!features.plugins?.['hebis-plugin']?.urlPath;
|
this.isHebisAvailable = !!features.plugins?.['hebis-plugin']?.urlPath;
|
||||||
@@ -359,10 +364,4 @@ export class SearchPageComponent implements OnInit, OnDestroy {
|
|||||||
this.searchStringChanged(term);
|
this.searchStringChanged(term);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
for (const subscription of this.subscriptions) {
|
|
||||||
subscription.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,12 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component, ContentChild, Input, OnDestroy, OnInit, TemplateRef} from '@angular/core';
|
import {Component, ContentChild, DestroyRef, inject, Input, OnInit, TemplateRef} from '@angular/core';
|
||||||
import {SCThings} from '@openstapps/core';
|
import {SCThings} from '@openstapps/core';
|
||||||
import {Subscription} from 'rxjs';
|
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
import {DataRoutingService} from '../data-routing.service';
|
import {DataRoutingService} from '../data-routing.service';
|
||||||
import {DataListContext} from './data-list.component';
|
import {DataListContext} from './data-list.component';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the list of items
|
* Shows the list of items
|
||||||
@@ -27,7 +27,7 @@ import {DataListContext} from './data-list.component';
|
|||||||
templateUrl: 'simple-data-list.html',
|
templateUrl: 'simple-data-list.html',
|
||||||
styleUrls: ['simple-data-list.scss'],
|
styleUrls: ['simple-data-list.scss'],
|
||||||
})
|
})
|
||||||
export class SimpleDataListComponent implements OnInit, OnDestroy {
|
export class SimpleDataListComponent implements OnInit {
|
||||||
@Input() items?: Promise<SCThings[] | undefined>;
|
@Input() items?: Promise<SCThings[] | undefined>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -51,28 +51,17 @@ export class SimpleDataListComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
skeletonItems = 6;
|
skeletonItems = 6;
|
||||||
|
|
||||||
/**
|
destroy$ = inject(DestroyRef);
|
||||||
* Array of all subscriptions to Observables
|
|
||||||
*/
|
|
||||||
subscriptions: Subscription[] = [];
|
|
||||||
|
|
||||||
constructor(protected router: Router, private readonly dataRoutingService: DataRoutingService) {}
|
constructor(protected router: Router, private readonly dataRoutingService: DataRoutingService) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
if (!this.autoRouting) return;
|
if (!this.autoRouting) return;
|
||||||
this.subscriptions.push(
|
this.dataRoutingService
|
||||||
this.dataRoutingService.itemSelectListener().subscribe(item => {
|
.itemSelectListener()
|
||||||
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
|
.subscribe(item => {
|
||||||
void this.router.navigate(['/data-detail', item.uid]);
|
void this.router.navigate(['/data-detail', item.uid]);
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove subscriptions when the component is removed
|
|
||||||
*/
|
|
||||||
ngOnDestroy() {
|
|
||||||
for (const sub of this.subscriptions) {
|
|
||||||
sub.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,42 +12,30 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
import {SCBuilding, SCFloor, SCPointOfInterest, SCRoom, SCThings} from '@openstapps/core';
|
import {SCBuilding, SCFloor, SCPointOfInterest, SCRoom, SCThings} from '@openstapps/core';
|
||||||
import {DataProvider} from '../../data.provider';
|
import {DataProvider} from '../../data.provider';
|
||||||
import {hasValidLocation, isSCFloor} from './place-types';
|
import {hasValidLocation, isSCFloor} from './place-types';
|
||||||
import {DataRoutingService} from '../../data-routing.service';
|
import {DataRoutingService} from '../../data-routing.service';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
import {Subscription} from 'rxjs';
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
@Component({
|
@Component({
|
||||||
providers: [DataProvider],
|
providers: [DataProvider],
|
||||||
styleUrls: ['place-detail-content.scss'],
|
styleUrls: ['place-detail-content.scss'],
|
||||||
selector: 'stapps-place-detail-content',
|
selector: 'stapps-place-detail-content',
|
||||||
templateUrl: 'place-detail-content.html',
|
templateUrl: 'place-detail-content.html',
|
||||||
})
|
})
|
||||||
export class PlaceDetailContentComponent implements OnInit, OnDestroy {
|
export class PlaceDetailContentComponent implements OnInit {
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
@Input() item: SCBuilding | SCRoom | SCPointOfInterest | SCFloor;
|
@Input() item: SCBuilding | SCRoom | SCPointOfInterest | SCFloor;
|
||||||
|
|
||||||
@Input() openAsModal = false;
|
@Input() openAsModal = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does it have valid location or not (for showing in in a map widget)
|
* Does it have a valid location or not (for showing in a map widget)
|
||||||
*/
|
*/
|
||||||
hasValidLocation = false;
|
hasValidLocation = false;
|
||||||
|
|
||||||
itemRouting: Subscription;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param item TODO
|
|
||||||
*/
|
|
||||||
hasCategories(item: SCThings): item is SCThings & {categories: string[]} {
|
hasCategories(item: SCThings): item is SCThings & {categories: string[]} {
|
||||||
return (item as {categories: string[]}).categories !== undefined;
|
return (item as {categories: string[]}).categories !== undefined;
|
||||||
}
|
}
|
||||||
@@ -67,16 +55,15 @@ export class PlaceDetailContentComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(dataRoutingService: DataRoutingService, router: Router) {
|
constructor(dataRoutingService: DataRoutingService, router: Router) {
|
||||||
this.itemRouting = dataRoutingService.itemSelectListener().subscribe(item => {
|
dataRoutingService
|
||||||
void router.navigate(['/data-detail', item.uid]);
|
.itemSelectListener()
|
||||||
});
|
.pipe(takeUntilDestroyed())
|
||||||
|
.subscribe(item => {
|
||||||
|
void router.navigate(['/data-detail', item.uid]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.hasValidLocation = !isSCFloor(this.item) && hasValidLocation(this.item);
|
this.hasValidLocation = !isSCFloor(this.item) && hasValidLocation(this.item);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
this.itemRouting.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,14 +13,14 @@
|
|||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import moment, {Moment} from 'moment';
|
import moment, {Moment} from 'moment';
|
||||||
import {AfterViewInit, Component, Input, OnDestroy} from '@angular/core';
|
import {AfterViewInit, Component, DestroyRef, inject, Input} from '@angular/core';
|
||||||
import {SCDish, SCISO8601Date, SCPlace} from '@openstapps/core';
|
import {SCDish, SCISO8601Date, SCPlace} from '@openstapps/core';
|
||||||
import {PlaceMensaService} from './place-mensa-service';
|
import {PlaceMensaService} from './place-mensa-service';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
import {Subscription} from 'rxjs';
|
|
||||||
import {IonRouterOutlet} from '@ionic/angular';
|
import {IonRouterOutlet} from '@ionic/angular';
|
||||||
import {DataRoutingService} from '../../../../data-routing.service';
|
import {DataRoutingService} from '../../../../data-routing.service';
|
||||||
import {groupBy} from '@openstapps/collection-utils';
|
import {groupBy} from '@openstapps/collection-utils';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO
|
* TODO
|
||||||
@@ -31,7 +31,7 @@ import {groupBy} from '@openstapps/collection-utils';
|
|||||||
templateUrl: 'place-mensa.html',
|
templateUrl: 'place-mensa.html',
|
||||||
styleUrls: ['place-mensa.scss'],
|
styleUrls: ['place-mensa.scss'],
|
||||||
})
|
})
|
||||||
export class PlaceMensaDetailComponent implements AfterViewInit, OnDestroy {
|
export class PlaceMensaDetailComponent implements AfterViewInit {
|
||||||
/**
|
/**
|
||||||
* Map of dishes for each day
|
* Map of dishes for each day
|
||||||
*/
|
*/
|
||||||
@@ -44,9 +44,6 @@ export class PlaceMensaDetailComponent implements AfterViewInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
@Input() displayRange = 7;
|
@Input() displayRange = 7;
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
@Input() item: SCPlace;
|
@Input() item: SCPlace;
|
||||||
|
|
||||||
@Input() openAsModal = false;
|
@Input() openAsModal = false;
|
||||||
@@ -61,10 +58,7 @@ export class PlaceMensaDetailComponent implements AfterViewInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
startingDay: Moment;
|
startingDay: Moment;
|
||||||
|
|
||||||
/**
|
destroy$ = inject(DestroyRef);
|
||||||
* Array of all subscriptions to Observables
|
|
||||||
*/
|
|
||||||
subscriptions: Subscription[] = [];
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly mensaService: PlaceMensaService,
|
private readonly mensaService: PlaceMensaService,
|
||||||
@@ -75,16 +69,14 @@ export class PlaceMensaDetailComponent implements AfterViewInit, OnDestroy {
|
|||||||
this.startingDay = moment().startOf('day');
|
this.startingDay = moment().startOf('day');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
if (!this.openAsModal) {
|
if (!this.openAsModal) {
|
||||||
this.subscriptions.push(
|
this.dataRoutingService
|
||||||
this.dataRoutingService.itemSelectListener().subscribe(item => {
|
.itemSelectListener()
|
||||||
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
|
.subscribe(item => {
|
||||||
void this.router.navigate(['/data-detail', item.uid]);
|
void this.router.navigate(['/data-detail', item.uid]);
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const dishesByDay = this.mensaService.getAllDishes(this.item, this.displayRange);
|
const dishesByDay = this.mensaService.getAllDishes(this.item, this.displayRange);
|
||||||
@@ -111,13 +103,4 @@ export class PlaceMensaDetailComponent implements AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove subscriptions when the component is removed
|
|
||||||
*/
|
|
||||||
ngOnDestroy() {
|
|
||||||
for (const sub of this.subscriptions) {
|
|
||||||
sub.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import {DataProvider} from '../data/data.provider';
|
|||||||
import {SettingsProvider} from '../settings/settings.provider';
|
import {SettingsProvider} from '../settings/settings.provider';
|
||||||
import {PositionService} from '../map/position.service';
|
import {PositionService} from '../map/position.service';
|
||||||
import {ConfigProvider} from '../config/config.provider';
|
import {ConfigProvider} from '../config/config.provider';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The page for showing favorites
|
* The page for showing favorites
|
||||||
@@ -71,23 +72,21 @@ export class FavoritesPageComponent extends SearchPageComponent implements OnIni
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
super.ngOnInit();
|
super.ngOnInit(false);
|
||||||
for (const subscription of this.subscriptions) {
|
|
||||||
subscription.unsubscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recreate subscriptions to handle different routing
|
// Recreate subscriptions to handle different routing
|
||||||
this.subscriptions.push(
|
combineLatest([
|
||||||
combineLatest([
|
this.queryTextChanged.pipe(
|
||||||
this.queryTextChanged.pipe(
|
debounceTime(this.searchQueryDueTime),
|
||||||
debounceTime(this.searchQueryDueTime),
|
distinctUntilChanged(),
|
||||||
distinctUntilChanged(),
|
startWith(this.queryText),
|
||||||
startWith(this.queryText),
|
),
|
||||||
),
|
this.contextMenuService.filterQueryChanged$.pipe(startWith(this.filterQuery)),
|
||||||
this.contextMenuService.filterQueryChanged$.pipe(startWith(this.filterQuery)),
|
this.contextMenuService.sortQueryChanged$.pipe(startWith(this.sortQuery)),
|
||||||
this.contextMenuService.sortQueryChanged$.pipe(startWith(this.sortQuery)),
|
this.favoritesService.favoritesChanged$,
|
||||||
this.favoritesService.favoritesChanged$,
|
])
|
||||||
]).subscribe(async query => {
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
|
.subscribe(async query => {
|
||||||
this.queryText = query[0];
|
this.queryText = query[0];
|
||||||
this.filterQuery = query[1];
|
this.filterQuery = query[1];
|
||||||
this.sortQuery = query[2];
|
this.sortQuery = query[2];
|
||||||
@@ -96,16 +95,21 @@ export class FavoritesPageComponent extends SearchPageComponent implements OnIni
|
|||||||
await this.fetchAndUpdateItems();
|
await this.fetchAndUpdateItems();
|
||||||
this.queryChanged.next();
|
this.queryChanged.next();
|
||||||
}
|
}
|
||||||
}),
|
});
|
||||||
this.settingsProvider.settingsActionChanged$.subscribe(({type, payload}) => {
|
this.settingsProvider.settingsActionChanged$
|
||||||
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
|
.subscribe(({type, payload}) => {
|
||||||
if (type === 'stapps.settings.changed') {
|
if (type === 'stapps.settings.changed') {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
const {category, name, value} = payload!;
|
const {category, name, value} = payload!;
|
||||||
this.logger.log(`received event "settings.changed" with category:
|
this.logger.log(`received event "settings.changed" with category:
|
||||||
${category}, name: ${name}, value: ${JSON.stringify(value)}`);
|
${category}, name: ${name}, value: ${JSON.stringify(value)}`);
|
||||||
}
|
}
|
||||||
}),
|
});
|
||||||
this.dataRoutingService.itemSelectListener().subscribe(item => {
|
this.dataRoutingService
|
||||||
|
.itemSelectListener()
|
||||||
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
|
.subscribe(item => {
|
||||||
if (this.itemRouting) {
|
if (this.itemRouting) {
|
||||||
if ([SCThingType.Book, SCThingType.Periodical, SCThingType.Article].includes(item.type)) {
|
if ([SCThingType.Book, SCThingType.Periodical, SCThingType.Article].includes(item.type)) {
|
||||||
void this.router.navigate([
|
void this.router.navigate([
|
||||||
@@ -116,8 +120,7 @@ export class FavoritesPageComponent extends SearchPageComponent implements OnIni
|
|||||||
void this.router.navigate(['data-detail', item.uid]);
|
void this.router.navigate(['data-detail', item.uid]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
import {ActivatedRoute, Router} from '@angular/router';
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
import {AlertController, AnimationController} from '@ionic/angular';
|
import {AlertController, AnimationController} from '@ionic/angular';
|
||||||
import {NGXLogger} from 'ngx-logger';
|
import {NGXLogger} from 'ngx-logger';
|
||||||
@@ -25,6 +25,7 @@ import {SearchPageComponent} from '../../data/list/search-page.component';
|
|||||||
import {HebisDataProvider} from '../hebis-data.provider';
|
import {HebisDataProvider} from '../hebis-data.provider';
|
||||||
import {PositionService} from '../../map/position.service';
|
import {PositionService} from '../../map/position.service';
|
||||||
import {ConfigProvider} from '../../config/config.provider';
|
import {ConfigProvider} from '../../config/config.provider';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HebisSearchPageComponent queries things and shows list of things as search results and filter as context menu
|
* HebisSearchPageComponent queries things and shows list of things as search results and filter as context menu
|
||||||
@@ -34,7 +35,7 @@ import {ConfigProvider} from '../../config/config.provider';
|
|||||||
templateUrl: 'hebis-search-page.html',
|
templateUrl: 'hebis-search-page.html',
|
||||||
styleUrls: ['../../data/list/search-page.scss'],
|
styleUrls: ['../../data/list/search-page.scss'],
|
||||||
})
|
})
|
||||||
export class HebisSearchPageComponent extends SearchPageComponent implements OnInit, OnDestroy {
|
export class HebisSearchPageComponent extends SearchPageComponent implements OnInit {
|
||||||
/**
|
/**
|
||||||
* If routing should be done if the user clicks on an item
|
* If routing should be done if the user clicks on an item
|
||||||
*/
|
*/
|
||||||
@@ -144,43 +145,42 @@ export class HebisSearchPageComponent extends SearchPageComponent implements OnI
|
|||||||
//this.fetchAndUpdateItems();
|
//this.fetchAndUpdateItems();
|
||||||
this.initialize();
|
this.initialize();
|
||||||
|
|
||||||
this.subscriptions.push(
|
combineLatest([
|
||||||
combineLatest([
|
this.queryTextChanged.pipe(
|
||||||
this.queryTextChanged.pipe(
|
debounceTime(this.searchQueryDueTime),
|
||||||
debounceTime(this.searchQueryDueTime),
|
distinctUntilChanged(),
|
||||||
distinctUntilChanged(),
|
startWith(this.queryText),
|
||||||
startWith(this.queryText),
|
),
|
||||||
),
|
])
|
||||||
]).subscribe(async query => {
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
|
.subscribe(async query => {
|
||||||
this.queryText = query[0];
|
this.queryText = query[0];
|
||||||
this.page = 0;
|
this.page = 0;
|
||||||
if (this.queryText?.length > 0 || this.showDefaultData) {
|
if (this.queryText?.length > 0 || this.showDefaultData) {
|
||||||
await this.fetchAndUpdateItems();
|
await this.fetchAndUpdateItems();
|
||||||
this.queryChanged.next();
|
this.queryChanged.next();
|
||||||
}
|
}
|
||||||
}),
|
});
|
||||||
this.settingsProvider.settingsActionChanged$.subscribe(({type, payload}) => {
|
this.settingsProvider.settingsActionChanged$
|
||||||
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
|
.subscribe(({type, payload}) => {
|
||||||
if (type === 'stapps.settings.changed') {
|
if (type === 'stapps.settings.changed') {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
const {category, name, value} = payload!;
|
const {category, name, value} = payload!;
|
||||||
this.logger.log(`received event "settings.changed" with category:
|
this.logger.log(`received event "settings.changed" with category:
|
||||||
${category}, name: ${name}, value: ${JSON.stringify(value)}`);
|
${category}, name: ${name}, value: ${JSON.stringify(value)}`);
|
||||||
}
|
}
|
||||||
}),
|
});
|
||||||
this.dataRoutingService.itemSelectListener().subscribe(async item => {
|
this.dataRoutingService
|
||||||
|
.itemSelectListener()
|
||||||
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
|
.subscribe(async item => {
|
||||||
if (this.itemRouting) {
|
if (this.itemRouting) {
|
||||||
void this.router.navigate([
|
void this.router.navigate([
|
||||||
'hebis-detail',
|
'hebis-detail',
|
||||||
(item.origin && 'originalId' in item.origin && item.origin['originalId']) || '',
|
(item.origin && 'originalId' in item.origin && item.origin['originalId']) || '',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
for (const subscription of this.subscriptions) {
|
|
||||||
subscription.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,20 +13,22 @@
|
|||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Location} from '@angular/common';
|
import {Location} from '@angular/common';
|
||||||
import {ChangeDetectorRef, Component, ElementRef, ViewChild} from '@angular/core';
|
import {ChangeDetectorRef, Component, DestroyRef, ElementRef, inject, OnInit, ViewChild} from '@angular/core';
|
||||||
import {ActivatedRoute, Router} from '@angular/router';
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
import {Keyboard} from '@capacitor/keyboard';
|
import {Keyboard} from '@capacitor/keyboard';
|
||||||
import {AlertController, IonRouterOutlet, ModalController} from '@ionic/angular';
|
import {AlertController, IonRouterOutlet, ModalController} from '@ionic/angular';
|
||||||
import {TranslateService} from '@ngx-translate/core';
|
import {TranslateService} from '@ngx-translate/core';
|
||||||
import {SCBuilding, SCPlace, SCRoom, SCSearchFilter, SCUuid} from '@openstapps/core';
|
import {SCBuilding, SCPlace, SCRoom, SCSearchFilter, SCUuid} from '@openstapps/core';
|
||||||
import {featureGroup, geoJSON, LatLng, Layer, Map, MapOptions, Marker, tileLayer} from 'leaflet';
|
import {featureGroup, geoJSON, LatLng, Layer, Map, MapOptions, Marker, tileLayer} from 'leaflet';
|
||||||
import {Subscription} from 'rxjs';
|
import {BehaviorSubject} from 'rxjs';
|
||||||
import {DataRoutingService} from '../../data/data-routing.service';
|
import {DataRoutingService} from '../../data/data-routing.service';
|
||||||
import {ContextMenuService} from '../../menu/context/context-menu.service';
|
import {ContextMenuService} from '../../menu/context/context-menu.service';
|
||||||
import {MapProvider} from '../map.provider';
|
import {MapProvider} from '../map.provider';
|
||||||
import {MapPosition, PositionService} from '../position.service';
|
import {MapPosition, PositionService} from '../position.service';
|
||||||
import {Geolocation, PermissionStatus} from '@capacitor/geolocation';
|
import {Geolocation, PermissionStatus} from '@capacitor/geolocation';
|
||||||
import {Capacitor} from '@capacitor/core';
|
import {Capacitor} from '@capacitor/core';
|
||||||
|
import {pauseWhen} from '../../../util/pause-when';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main page of the map
|
* The main page of the map
|
||||||
@@ -36,7 +38,7 @@ import {Capacitor} from '@capacitor/core';
|
|||||||
templateUrl: './map-page.html',
|
templateUrl: './map-page.html',
|
||||||
providers: [ContextMenuService],
|
providers: [ContextMenuService],
|
||||||
})
|
})
|
||||||
export class MapPageComponent {
|
export class MapPageComponent implements OnInit {
|
||||||
/**
|
/**
|
||||||
* Default map zoom level
|
* Default map zoom level
|
||||||
*/
|
*/
|
||||||
@@ -115,10 +117,9 @@ export class MapPageComponent {
|
|||||||
*/
|
*/
|
||||||
queryText: string;
|
queryText: string;
|
||||||
|
|
||||||
/**
|
isNotInView$ = new BehaviorSubject(true);
|
||||||
* Subscriptions used by the page
|
|
||||||
*/
|
destroy$ = inject(DestroyRef);
|
||||||
subscriptions: Subscription[] = [];
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private translateService: TranslateService,
|
private translateService: TranslateService,
|
||||||
@@ -148,6 +149,34 @@ export class MapPageComponent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.dataRoutingService
|
||||||
|
.itemSelectListener()
|
||||||
|
.pipe(pauseWhen(this.isNotInView$), takeUntilDestroyed(this.destroy$))
|
||||||
|
.subscribe(async item => {
|
||||||
|
// in case the list item is clicked
|
||||||
|
if (this.items.length > 1) {
|
||||||
|
await Promise.all([this.modalController.dismiss(), this.showItem(item.uid)]);
|
||||||
|
} else {
|
||||||
|
void this.router.navigate(['/data-detail', item.uid]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.positionService
|
||||||
|
.watchCurrentLocation({enableHighAccuracy: true, maximumAge: 1000})
|
||||||
|
.pipe(pauseWhen(this.isNotInView$), takeUntilDestroyed(this.destroy$))
|
||||||
|
.subscribe({
|
||||||
|
next: (position: MapPosition) => {
|
||||||
|
this.position = position;
|
||||||
|
this.positionMarker = MapProvider.getPositionMarker(position, 'stapps-device-location', 32);
|
||||||
|
},
|
||||||
|
error: async _error => {
|
||||||
|
this.locationStatus = await Geolocation.checkPermissions();
|
||||||
|
// eslint-disable-next-line unicorn/no-null
|
||||||
|
this.position = null;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Animate to coordinates
|
* Animate to coordinates
|
||||||
* @param latLng Coordinates to animate to
|
* @param latLng Coordinates to animate to
|
||||||
@@ -257,47 +286,18 @@ 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() {
|
||||||
|
this.isNotInView$.next(false);
|
||||||
if (this.positionService.position) {
|
if (this.positionService.position) {
|
||||||
this.position = this.positionService.position;
|
this.position = this.positionService.position;
|
||||||
this.positionMarker = MapProvider.getPositionMarker(this.position, 'stapps-device-location', 32);
|
this.positionMarker = MapProvider.getPositionMarker(this.position, 'stapps-device-location', 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.subscriptions.push(
|
|
||||||
this.dataRoutingService.itemSelectListener().subscribe(async item => {
|
|
||||||
// in case the list item is clicked
|
|
||||||
if (this.items.length > 1) {
|
|
||||||
await Promise.all([this.modalController.dismiss(), this.showItem(item.uid)]);
|
|
||||||
} else {
|
|
||||||
void this.router.navigate(['/data-detail', item.uid]);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
this.positionService
|
|
||||||
.watchCurrentLocation(this.constructor.name, {enableHighAccuracy: true, maximumAge: 1000})
|
|
||||||
.subscribe({
|
|
||||||
next: (position: MapPosition) => {
|
|
||||||
this.position = position;
|
|
||||||
this.positionMarker = MapProvider.getPositionMarker(position, 'stapps-device-location', 32);
|
|
||||||
},
|
|
||||||
error: async _error => {
|
|
||||||
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)
|
||||||
this.locationStatus = await Geolocation.checkPermissions();
|
this.locationStatus = await Geolocation.checkPermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Unsubscribe from all subscriptions when user leaves page
|
|
||||||
*/
|
|
||||||
ionViewWillLeave() {
|
ionViewWillLeave() {
|
||||||
void this.positionService.clearWatcher(this.constructor.name);
|
this.isNotInView$.next(true);
|
||||||
for (const sub of this.subscriptions) {
|
|
||||||
sub.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -322,12 +322,12 @@ export class MapPageComponent {
|
|||||||
this.addToMap(this.items, true, uid !== null);
|
this.addToMap(this.items, true, uid !== null);
|
||||||
this.contextMenuService.updateContextFilter(response.facets);
|
this.contextMenuService.updateContextFilter(response.facets);
|
||||||
|
|
||||||
this.subscriptions.push(
|
this.contextMenuService.filterQueryChanged$
|
||||||
this.contextMenuService.filterQueryChanged$.subscribe(query => {
|
.pipe(pauseWhen(this.isNotInView$), takeUntilDestroyed(this.destroy$))
|
||||||
|
.subscribe(query => {
|
||||||
this.filterQuery = query;
|
this.filterQuery = query;
|
||||||
this.fetchAndUpdateItems(false, true);
|
this.fetchAndUpdateItems(false, true);
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
|
|
||||||
this.distance = this.positionService.getDistance(this.items[0].geo.point);
|
this.distance = this.positionService.getDistance(this.items[0].geo.point);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import {StorageModule} from '../storage/storage.module';
|
|||||||
import {MapPosition, PositionService} from './position.service';
|
import {MapPosition, PositionService} from './position.service';
|
||||||
import {ConfigProvider} from '../config/config.provider';
|
import {ConfigProvider} from '../config/config.provider';
|
||||||
import {LoggerTestingModule} from 'ngx-logger/testing';
|
import {LoggerTestingModule} from 'ngx-logger/testing';
|
||||||
|
import {firstValueFrom} from 'rxjs';
|
||||||
|
import {Geolocation} from '@capacitor/geolocation';
|
||||||
|
|
||||||
describe('PositionService', () => {
|
describe('PositionService', () => {
|
||||||
let positionService: PositionService;
|
let positionService: PositionService;
|
||||||
@@ -54,31 +56,17 @@ describe('PositionService', () => {
|
|||||||
expect(currentLocation).toEqual(sampleMapPosition);
|
expect(currentLocation).toEqual(sampleMapPosition);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should continuously provide (watch) location of the device', done => {
|
it('should continuously provide (watch) location of the device', async () => {
|
||||||
positionService.watchCurrentLocation('testCaller').subscribe(location => {
|
expect(await firstValueFrom(positionService.watchCurrentLocation())).toBeDefined();
|
||||||
expect(location).toBeDefined();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should stop to continuously provide (watch) location of the device', done => {
|
it('should stop to continuously provide (watch) location of the device', async () => {
|
||||||
positionService.watchers.set(
|
const watchPosition = spyOn(Geolocation, 'watchPosition').and.resolveTo('abc');
|
||||||
'clearWatch',
|
const clearWatch = spyOn(Geolocation, 'clearWatch').and.callThrough();
|
||||||
new Promise(resolve => {
|
const subscription = positionService.watchCurrentLocation().subscribe();
|
||||||
setTimeout(function () {
|
expect(watchPosition).toHaveBeenCalled();
|
||||||
resolve(`watcherID123`);
|
subscription.unsubscribe();
|
||||||
}, 20);
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
}),
|
expect(clearWatch).toHaveBeenCalledWith({id: 'abc'});
|
||||||
);
|
|
||||||
positionService
|
|
||||||
.clearWatcher('clearWatch')
|
|
||||||
.then(result => {
|
|
||||||
expect(result).toBeUndefined();
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
expect(error).toBeUndefined();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -85,11 +85,10 @@ export class PositionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Watches (continuously gets) current coordinates information of the device
|
* Watches (continuously gets) the 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(caller: string, options: PositionOptions = {}): Observable<MapPosition> {
|
watchCurrentLocation(options: PositionOptions = {}): Observable<MapPosition> {
|
||||||
return new Observable(subscriber => {
|
return new Observable(subscriber => {
|
||||||
const watcherID = Geolocation.watchPosition(options, (position, error) => {
|
const watcherID = Geolocation.watchPosition(options, (position, error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -106,18 +105,15 @@ export class PositionService {
|
|||||||
subscriber.next(this.position);
|
subscriber.next(this.position);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.watchers.set(caller, watcherID);
|
watcherID.then(console.log);
|
||||||
|
return {
|
||||||
|
unsubscribe() {
|
||||||
|
watcherID.then(id => {
|
||||||
|
console.log(id);
|
||||||
|
void Geolocation.clearWatch({id});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,12 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component, Input, OnDestroy} from '@angular/core';
|
import {Component, Input} from '@angular/core';
|
||||||
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
|
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
|
||||||
import {SCLanguage, SCThingTranslator, SCThingType, SCTranslations} from '@openstapps/core';
|
import {SCLanguage, SCThingTranslator, SCThingType, SCTranslations} from '@openstapps/core';
|
||||||
import {Subscription} from 'rxjs';
|
|
||||||
import {ContextMenuService} from './context-menu.service';
|
import {ContextMenuService} from './context-menu.service';
|
||||||
import {FilterContext, FilterFacet, SortContext, SortContextOption} from './context-type.js';
|
import {FilterContext, FilterFacet, SortContext, SortContextOption} from './context-type.js';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The context menu
|
* The context menu
|
||||||
@@ -32,7 +32,7 @@ import {FilterContext, FilterFacet, SortContext, SortContextOption} from './cont
|
|||||||
selector: 'stapps-context',
|
selector: 'stapps-context',
|
||||||
templateUrl: 'context-menu.html',
|
templateUrl: 'context-menu.html',
|
||||||
})
|
})
|
||||||
export class ContextMenuComponent implements OnDestroy {
|
export class ContextMenuComponent {
|
||||||
/**
|
/**
|
||||||
* Id of the content the menu is used for
|
* Id of the content the menu is used for
|
||||||
*/
|
*/
|
||||||
@@ -77,11 +77,6 @@ export class ContextMenuComponent implements OnDestroy {
|
|||||||
*/
|
*/
|
||||||
sortOption: SortContext;
|
sortOption: SortContext;
|
||||||
|
|
||||||
/**
|
|
||||||
* Array of all Subscriptions
|
|
||||||
*/
|
|
||||||
subscriptions: Subscription[] = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core translator
|
* Core translator
|
||||||
*/
|
*/
|
||||||
@@ -94,18 +89,16 @@ export class ContextMenuComponent implements OnDestroy {
|
|||||||
this.language = this.translateService.currentLang as keyof SCTranslations<SCLanguage>;
|
this.language = this.translateService.currentLang as keyof SCTranslations<SCLanguage>;
|
||||||
this.translator = new SCThingTranslator(this.language);
|
this.translator = new SCThingTranslator(this.language);
|
||||||
|
|
||||||
this.subscriptions.push(
|
this.translateService.onLangChange.pipe(takeUntilDestroyed()).subscribe((event: LangChangeEvent) => {
|
||||||
this.translateService.onLangChange.subscribe((event: LangChangeEvent) => {
|
this.language = event.lang as keyof SCTranslations<SCLanguage>;
|
||||||
this.language = event.lang as keyof SCTranslations<SCLanguage>;
|
this.translator = new SCThingTranslator(this.language);
|
||||||
this.translator = new SCThingTranslator(this.language);
|
});
|
||||||
}),
|
this.contextMenuService.filterContextChanged$.pipe(takeUntilDestroyed()).subscribe(filterContext => {
|
||||||
this.contextMenuService.filterContextChanged$.subscribe(filterContext => {
|
this.filterOption = filterContext;
|
||||||
this.filterOption = filterContext;
|
});
|
||||||
}),
|
this.contextMenuService.sortOptions.pipe(takeUntilDestroyed()).subscribe(sortContext => {
|
||||||
this.contextMenuService.sortOptions.subscribe(sortContext => {
|
this.sortOption = sortContext;
|
||||||
this.sortOption = sortContext;
|
});
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -122,15 +115,6 @@ export class ContextMenuComponent implements OnDestroy {
|
|||||||
return this.translator.translatedPropertyValue(onlyForType, field, key);
|
return this.translator.translatedPropertyValue(onlyForType, field, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Unsubscribe from Observables
|
|
||||||
*/
|
|
||||||
ngOnDestroy() {
|
|
||||||
for (const sub of this.subscriptions) {
|
|
||||||
sub.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets filter options
|
* Resets filter options
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -12,40 +12,35 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
import {Component, ElementRef, HostBinding, ViewChild} from '@angular/core';
|
||||||
import {Component, ElementRef, HostBinding, OnDestroy, ViewChild} from '@angular/core';
|
|
||||||
import {InternetConnectionService} from '../../../util/internet-connection.service';
|
import {InternetConnectionService} from '../../../util/internet-connection.service';
|
||||||
import {Subscription} from 'rxjs';
|
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
import {NGXLogger} from 'ngx-logger';
|
import {NGXLogger} from 'ngx-logger';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'stapps-offline-notice',
|
selector: 'stapps-offline-notice',
|
||||||
templateUrl: 'offline-notice.html',
|
templateUrl: 'offline-notice.html',
|
||||||
styleUrls: ['offline-notice.scss'],
|
styleUrls: ['offline-notice.scss'],
|
||||||
})
|
})
|
||||||
export class OfflineNoticeComponent implements OnDestroy {
|
export class OfflineNoticeComponent {
|
||||||
@HostBinding('class.is-offline') isOffline = false;
|
@HostBinding('class.is-offline') isOffline = false;
|
||||||
|
|
||||||
@HostBinding('class.has-error') hasError = false;
|
@HostBinding('class.has-error') hasError = false;
|
||||||
|
|
||||||
@ViewChild('spinIcon', {read: ElementRef}) spinIcon: ElementRef;
|
@ViewChild('spinIcon', {read: ElementRef}) spinIcon: ElementRef;
|
||||||
|
|
||||||
readonly subscriptions: Subscription[];
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly offlineProvider: InternetConnectionService,
|
readonly offlineProvider: InternetConnectionService,
|
||||||
readonly router: Router,
|
readonly router: Router,
|
||||||
readonly logger: NGXLogger,
|
readonly logger: NGXLogger,
|
||||||
) {
|
) {
|
||||||
this.subscriptions = [
|
this.offlineProvider.offline$.pipe(takeUntilDestroyed()).subscribe(isOffline => {
|
||||||
this.offlineProvider.offline$.subscribe(isOffline => {
|
this.isOffline = isOffline;
|
||||||
this.isOffline = isOffline;
|
});
|
||||||
}),
|
this.offlineProvider.error$.pipe(takeUntilDestroyed()).subscribe(hasError => {
|
||||||
this.offlineProvider.error$.subscribe(hasError => {
|
this.hasError = hasError;
|
||||||
this.hasError = hasError;
|
});
|
||||||
}),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
retry() {
|
retry() {
|
||||||
@@ -54,10 +49,4 @@ export class OfflineNoticeComponent implements OnDestroy {
|
|||||||
this.spinIcon.nativeElement.classList.add('spin');
|
this.spinIcon.nativeElement.classList.add('spin');
|
||||||
this.offlineProvider.retry();
|
this.offlineProvider.retry();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
for (const subscription of this.subscriptions) {
|
|
||||||
subscription.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,28 +12,26 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Directive, ElementRef, Input, OnDestroy, OnInit, Renderer2} from '@angular/core';
|
import {DestroyRef, Directive, ElementRef, inject, Input, OnInit, Renderer2} from '@angular/core';
|
||||||
import {AnimationController, NavController} from '@ionic/angular';
|
import {AnimationController, NavController} from '@ionic/angular';
|
||||||
import {Router, RouterEvent} from '@angular/router';
|
import {Router, RouterEvent} from '@angular/router';
|
||||||
import {tabsTransition} from './tabs-transition';
|
import {tabsTransition} from './tabs-transition';
|
||||||
import {Subscription} from 'rxjs';
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[rootLink]',
|
selector: '[rootLink]',
|
||||||
})
|
})
|
||||||
export class RootLinkDirective implements OnInit, OnDestroy {
|
export class RootLinkDirective implements OnInit {
|
||||||
@Input() rootLink: string;
|
@Input() rootLink: string;
|
||||||
|
|
||||||
@Input() redirectedFrom: string;
|
@Input() redirectedFrom: string;
|
||||||
|
|
||||||
dispose: () => void;
|
|
||||||
|
|
||||||
subscriptions: Subscription[] = [];
|
|
||||||
|
|
||||||
private readonly classNames = ['tab-selected', 'link-active'];
|
private readonly classNames = ['tab-selected', 'link-active'];
|
||||||
|
|
||||||
private needsInit = true;
|
private needsInit = true;
|
||||||
|
|
||||||
|
destroy$ = inject(DestroyRef);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private element: ElementRef,
|
private element: ElementRef,
|
||||||
private renderer: Renderer2,
|
private renderer: Renderer2,
|
||||||
@@ -52,28 +50,28 @@ export class RootLinkDirective implements OnInit, OnDestroy {
|
|||||||
this.needsInit = false;
|
this.needsInit = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.subscriptions.push(
|
this.router.events.pipe(takeUntilDestroyed(this.destroy$)).subscribe(event => {
|
||||||
this.router.events.subscribe(event => {
|
if (
|
||||||
if (
|
event instanceof RouterEvent &&
|
||||||
event instanceof RouterEvent &&
|
// @ts-expect-error access private member
|
||||||
// @ts-expect-error access private member
|
(this.navController.direction === 'root' || this.needsInit)
|
||||||
(this.navController.direction === 'root' || this.needsInit)
|
) {
|
||||||
) {
|
if (event.url === this.rootLink || (this.redirectedFrom && event.url === this.redirectedFrom)) {
|
||||||
if (event.url === this.rootLink || (this.redirectedFrom && event.url === this.redirectedFrom)) {
|
this.setActive();
|
||||||
this.setActive();
|
} else {
|
||||||
} else {
|
this.setInactive();
|
||||||
this.setInactive();
|
|
||||||
}
|
|
||||||
this.needsInit = false;
|
|
||||||
}
|
}
|
||||||
|
this.needsInit = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.destroy$.onDestroy(
|
||||||
|
this.renderer.listen(this.element.nativeElement, 'click', () => {
|
||||||
|
this.setActive();
|
||||||
|
this.navController.setDirection('root', true, 'back', animation);
|
||||||
|
void this.router.navigate([this.rootLink]);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.dispose = this.renderer.listen(this.element.nativeElement, 'click', () => {
|
|
||||||
this.setActive();
|
|
||||||
this.navController.setDirection('root', true, 'back', animation);
|
|
||||||
void this.router.navigate([this.rootLink]);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setActive() {
|
setActive() {
|
||||||
@@ -87,11 +85,4 @@ export class RootLinkDirective implements OnInit, OnDestroy {
|
|||||||
this.renderer.removeClass(this.element.nativeElement, className);
|
this.renderer.removeClass(this.element.nativeElement, className);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
this.dispose();
|
|
||||||
for (const subscription of this.subscriptions) {
|
|
||||||
subscription.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,20 +12,20 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
import {Component, DestroyRef, inject, Input, OnInit} from '@angular/core';
|
||||||
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
|
|
||||||
import {SCSection} from '../../../../config/profile-page-sections';
|
import {SCSection} from '../../../../config/profile-page-sections';
|
||||||
import {AuthHelperService} from '../../auth/auth-helper.service';
|
import {AuthHelperService} from '../../auth/auth-helper.service';
|
||||||
import {Observable, Subscription} from 'rxjs';
|
import {Observable} from 'rxjs';
|
||||||
import {SCAuthorizationProviderType} from '@openstapps/core';
|
import {SCAuthorizationProviderType} from '@openstapps/core';
|
||||||
import Swiper from 'swiper';
|
import Swiper from 'swiper';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'stapps-profile-page-section',
|
selector: 'stapps-profile-page-section',
|
||||||
templateUrl: 'profile-page-section.html',
|
templateUrl: 'profile-page-section.html',
|
||||||
styleUrls: ['profile-page-section.scss'],
|
styleUrls: ['profile-page-section.scss'],
|
||||||
})
|
})
|
||||||
export class ProfilePageSectionComponent implements OnInit, OnDestroy {
|
export class ProfilePageSectionComponent implements OnInit {
|
||||||
@Input() item: SCSection;
|
@Input() item: SCSection;
|
||||||
|
|
||||||
@Input() minSlideWidth = 110;
|
@Input() minSlideWidth = 110;
|
||||||
@@ -36,8 +36,6 @@ export class ProfilePageSectionComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
isBeginning = true;
|
isBeginning = true;
|
||||||
|
|
||||||
subscriptions: Subscription[] = [];
|
|
||||||
|
|
||||||
slidesPerView: number;
|
slidesPerView: number;
|
||||||
|
|
||||||
slidesFillScreen = false;
|
slidesFillScreen = false;
|
||||||
@@ -53,15 +51,17 @@ export class ProfilePageSectionComponent implements OnInit, OnDestroy {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
destroy$ = inject(DestroyRef);
|
||||||
|
|
||||||
constructor(private authHelper: AuthHelperService) {}
|
constructor(private authHelper: AuthHelperService) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
if (this.item.authProvider) {
|
if (this.item.authProvider) {
|
||||||
this.subscriptions.push(
|
this.data[this.item.authProvider].loggedIn$
|
||||||
this.data[this.item.authProvider].loggedIn$.subscribe(loggedIn => {
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
|
.subscribe(loggedIn => {
|
||||||
this.isLoggedIn = loggedIn;
|
this.isLoggedIn = loggedIn;
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,10 +96,4 @@ export class ProfilePageSectionComponent implements OnInit, OnDestroy {
|
|||||||
await this.authHelper.getProvider(providerType).signOut();
|
await this.authHelper.getProvider(providerType).signOut();
|
||||||
await this.authHelper.endBrowserSession(providerType);
|
await this.authHelper.endBrowserSession(providerType);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
for (const subscription of this.subscriptions) {
|
|
||||||
subscription.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,8 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {Observable, of, Subscription} from 'rxjs';
|
import {firstValueFrom, Observable, of, Subscription} from 'rxjs';
|
||||||
import {AuthHelperService} from '../../auth/auth-helper.service';
|
import {AuthHelperService} from '../../auth/auth-helper.service';
|
||||||
import {SCAuthorizationProviderType, SCDateSeries, SCUserConfiguration} from '@openstapps/core';
|
import {SCAuthorizationProviderType, SCDateSeries, SCUserConfiguration} from '@openstapps/core';
|
||||||
import {ActivatedRoute} from '@angular/router';
|
import {ActivatedRoute} from '@angular/router';
|
||||||
@@ -93,22 +92,20 @@ export class ProfilePageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getMyCourses() {
|
async getMyCourses() {
|
||||||
const uuidSubscription = this.scheduleProvider.uuids$.subscribe(async result => {
|
const result = await firstValueFrom(this.scheduleProvider.uuids$);
|
||||||
const courses = await this.scheduleProvider.getDateSeries(result);
|
const courses = await this.scheduleProvider.getDateSeries(result);
|
||||||
|
|
||||||
for (const course of courses.dates) {
|
for (const course of courses.dates) {
|
||||||
for (const date of course.dates) {
|
for (const date of course.dates) {
|
||||||
if (moment(date).startOf('day').format() === this.todayDate) {
|
if (moment(date).startOf('day').format() === this.todayDate) {
|
||||||
this.myCoursesToday[this.myCoursesToday.length] = {
|
this.myCoursesToday[this.myCoursesToday.length] = {
|
||||||
startTime: moment(date).format('LT'),
|
startTime: moment(date).format('LT'),
|
||||||
endTime: moment(date).add(course.duration).format('LT'),
|
endTime: moment(date).add(course.duration).format('LT'),
|
||||||
course,
|
course,
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uuidSubscription.unsubscribe();
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async signIn(providerType: SCAuthorizationProviderType) {
|
async signIn(providerType: SCAuthorizationProviderType) {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {AfterViewInit, Component, Input, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
import {AfterViewInit, Component, Input, OnInit, ViewChild} from '@angular/core';
|
||||||
import {ActivatedRoute} from '@angular/router';
|
import {ActivatedRoute} from '@angular/router';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {materialFade, materialManualFade, materialSharedAxisX} from '../../../animation/material-motion';
|
import {materialFade, materialManualFade, materialSharedAxisX} from '../../../animation/material-motion';
|
||||||
@@ -22,6 +22,7 @@ import {CalendarComponent} from './components/calendar.component';
|
|||||||
import {CalendarService} from '../../calendar/calendar.service';
|
import {CalendarService} from '../../calendar/calendar.service';
|
||||||
import {InfiniteSwiperComponent} from './grid/infinite-swiper.component';
|
import {InfiniteSwiperComponent} from './grid/infinite-swiper.component';
|
||||||
import {IonContent} from '@ionic/angular';
|
import {IonContent} from '@ionic/angular';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that displays the schedule
|
* Component that displays the schedule
|
||||||
@@ -32,7 +33,7 @@ import {IonContent} from '@ionic/angular';
|
|||||||
styleUrls: ['calendar-view.scss', './components/calendar-component.scss'],
|
styleUrls: ['calendar-view.scss', './components/calendar-component.scss'],
|
||||||
animations: [materialFade, materialSharedAxisX, materialManualFade],
|
animations: [materialFade, materialSharedAxisX, materialManualFade],
|
||||||
})
|
})
|
||||||
export class CalendarViewComponent extends CalendarComponent implements OnInit, OnDestroy, AfterViewInit {
|
export class CalendarViewComponent extends CalendarComponent implements OnInit, AfterViewInit {
|
||||||
@ViewChild('mainSwiper') mainSwiper: InfiniteSwiperComponent;
|
@ViewChild('mainSwiper') mainSwiper: InfiniteSwiperComponent;
|
||||||
|
|
||||||
@ViewChild('headerSwiper') headerSwiper: InfiniteSwiperComponent;
|
@ViewChild('headerSwiper') headerSwiper: InfiniteSwiperComponent;
|
||||||
@@ -73,11 +74,8 @@ export class CalendarViewComponent extends CalendarComponent implements OnInit,
|
|||||||
* Initialize
|
* Initialize
|
||||||
*/
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
super.onInit();
|
super.ngOnInit();
|
||||||
if (this.calendarServiceSubscription) {
|
this.calendarService.goToDateClicked.pipe(takeUntilDestroyed(this.destroy$)).subscribe(async newIndex => {
|
||||||
this.calendarServiceSubscription.unsubscribe();
|
|
||||||
}
|
|
||||||
this.calendarServiceSubscription = this.calendarService.goToDateClicked.subscribe(async newIndex => {
|
|
||||||
await this.mainSwiper.goToIndex(newIndex);
|
await this.mainSwiper.goToIndex(newIndex);
|
||||||
this.setDateRange(newIndex);
|
this.setDateRange(newIndex);
|
||||||
await this.scrollCursorIntoView(this.content);
|
await this.scrollCursorIntoView(this.content);
|
||||||
@@ -88,13 +86,6 @@ export class CalendarViewComponent extends CalendarComponent implements OnInit,
|
|||||||
void this.scrollCursorIntoView(this.content);
|
void this.scrollCursorIntoView(this.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* OnDestroy
|
|
||||||
*/
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load events
|
* Load events
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
|
import {Component, DestroyRef, inject, Input, OnInit} from '@angular/core';
|
||||||
import {ActivatedRoute} from '@angular/router';
|
import {ActivatedRoute} from '@angular/router';
|
||||||
import {SCISO8601Date, SCUuid} from '@openstapps/core';
|
import {SCISO8601Date, SCUuid} from '@openstapps/core';
|
||||||
import moment, {Moment} from 'moment';
|
import moment, {Moment} from 'moment';
|
||||||
@@ -22,9 +22,9 @@ import {ScheduleEvent, ScheduleResponsiveBreakpoint} from '../schema/schema';
|
|||||||
import {SwiperComponent} from 'swiper/angular';
|
import {SwiperComponent} from 'swiper/angular';
|
||||||
import {InfiniteSwiperComponent} from '../grid/infinite-swiper.component';
|
import {InfiniteSwiperComponent} from '../grid/infinite-swiper.component';
|
||||||
import {IonContent, IonDatetime} from '@ionic/angular';
|
import {IonContent, IonDatetime} from '@ionic/angular';
|
||||||
import {Subscription} from 'rxjs';
|
|
||||||
import {CalendarService} from '../../../calendar/calendar.service';
|
import {CalendarService} from '../../../calendar/calendar.service';
|
||||||
import {getScheduleCursorOffset} from '../grid/schedule-cursor-offset';
|
import {getScheduleCursorOffset} from '../grid/schedule-cursor-offset';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that displays the schedule
|
* Component that displays the schedule
|
||||||
@@ -35,7 +35,7 @@ import {getScheduleCursorOffset} from '../grid/schedule-cursor-offset';
|
|||||||
styleUrls: ['calendar-component.scss'],
|
styleUrls: ['calendar-component.scss'],
|
||||||
animations: [materialFade, materialSharedAxisX, materialManualFade],
|
animations: [materialFade, materialSharedAxisX, materialManualFade],
|
||||||
})
|
})
|
||||||
export class CalendarComponent implements OnInit, OnDestroy {
|
export class CalendarComponent implements OnInit {
|
||||||
/**
|
/**
|
||||||
* The day that the schedule started out on
|
* The day that the schedule started out on
|
||||||
*/
|
*/
|
||||||
@@ -52,8 +52,6 @@ export class CalendarComponent implements OnInit, OnDestroy {
|
|||||||
endDate: '',
|
endDate: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
calendarServiceSubscription: Subscription;
|
|
||||||
|
|
||||||
prevHeaderIndex = 0;
|
prevHeaderIndex = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,15 +96,12 @@ export class CalendarComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
@Input() uuids: SCUuid[];
|
@Input() uuids: SCUuid[];
|
||||||
|
|
||||||
/**
|
|
||||||
* UUID subscription
|
|
||||||
*/
|
|
||||||
uuidSubscription: Subscription;
|
|
||||||
|
|
||||||
@Input() useInfiniteSwiper = true;
|
@Input() useInfiniteSwiper = true;
|
||||||
|
|
||||||
@Input() weekDates: Array<Moment>;
|
@Input() weekDates: Array<Moment>;
|
||||||
|
|
||||||
|
destroy$ = inject(DestroyRef);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected readonly activatedRoute: ActivatedRoute,
|
protected readonly activatedRoute: ActivatedRoute,
|
||||||
protected readonly calendarService: CalendarService,
|
protected readonly calendarService: CalendarService,
|
||||||
@@ -114,14 +109,6 @@ export class CalendarComponent implements OnInit, OnDestroy {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.onInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
this.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
onInit() {
|
|
||||||
let dayString: string | number | null = this.activatedRoute.snapshot.paramMap.get('date');
|
let dayString: string | number | null = this.activatedRoute.snapshot.paramMap.get('date');
|
||||||
if (dayString == undefined || dayString === 'now') {
|
if (dayString == undefined || dayString === 'now') {
|
||||||
const fragments = window.location.href.split('/');
|
const fragments = window.location.href.split('/');
|
||||||
@@ -133,7 +120,7 @@ export class CalendarComponent implements OnInit, OnDestroy {
|
|||||||
this.baselineDate = moment(dayString).startOf('day');
|
this.baselineDate = moment(dayString).startOf('day');
|
||||||
|
|
||||||
this.initialSlideIndex = new Promise(resolve => {
|
this.initialSlideIndex = new Promise(resolve => {
|
||||||
this.uuidSubscription = this.scheduleProvider.uuids$.subscribe(async result => {
|
this.scheduleProvider.uuids$.pipe(takeUntilDestroyed(this.destroy$)).subscribe(async result => {
|
||||||
this.uuids = result;
|
this.uuids = result;
|
||||||
resolve(await this.loadEvents());
|
resolve(await this.loadEvents());
|
||||||
});
|
});
|
||||||
@@ -150,13 +137,6 @@ export class CalendarComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onDestroy() {
|
|
||||||
this.uuidSubscription.unsubscribe();
|
|
||||||
if (this.calendarServiceSubscription) {
|
|
||||||
this.calendarServiceSubscription.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get date from baseline date and index of current slide.
|
* Get date from baseline date and index of current slide.
|
||||||
* @param index number
|
* @param index number
|
||||||
|
|||||||
@@ -12,14 +12,14 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
|
import {Component, DestroyRef, inject, Input, OnInit} from '@angular/core';
|
||||||
import {SCDateSeries, SCUuid} from '@openstapps/core';
|
import {SCDateSeries, SCUuid} from '@openstapps/core';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {Subscription} from 'rxjs';
|
|
||||||
import {materialFade} from '../../../animation/material-motion';
|
import {materialFade} from '../../../animation/material-motion';
|
||||||
import {ScheduleProvider} from '../../calendar/schedule.provider';
|
import {ScheduleProvider} from '../../calendar/schedule.provider';
|
||||||
import {ScheduleEvent} from './schema/schema';
|
import {ScheduleEvent} from './schema/schema';
|
||||||
import {groupBy, omit, stringSortBy} from '@openstapps/collection-utils';
|
import {groupBy, omit, stringSortBy} from '@openstapps/collection-utils';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A single event
|
* A single event
|
||||||
@@ -31,7 +31,7 @@ export interface ScheduleSingleEvent {
|
|||||||
day: string;
|
day: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event the date is referring to
|
* The event the date is referring to
|
||||||
*/
|
*/
|
||||||
event: ScheduleEvent;
|
event: ScheduleEvent;
|
||||||
}
|
}
|
||||||
@@ -45,12 +45,7 @@ export interface ScheduleSingleEvent {
|
|||||||
styleUrls: ['schedule-single-events.scss'],
|
styleUrls: ['schedule-single-events.scss'],
|
||||||
animations: [materialFade],
|
animations: [materialFade],
|
||||||
})
|
})
|
||||||
export class ScheduleSingleEventsComponent implements OnInit, OnDestroy {
|
export class ScheduleSingleEventsComponent implements OnInit {
|
||||||
/**
|
|
||||||
* UUID subscription
|
|
||||||
*/
|
|
||||||
private _uuidSubscription: Subscription;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The events to display
|
* The events to display
|
||||||
*/
|
*/
|
||||||
@@ -66,6 +61,8 @@ export class ScheduleSingleEventsComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
@Input() scale = 60;
|
@Input() scale = 60;
|
||||||
|
|
||||||
|
destroy$ = inject(DestroyRef);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sorts dates to a list of days with events on each
|
* Sorts dates to a list of days with events on each
|
||||||
*/
|
*/
|
||||||
@@ -116,24 +113,13 @@ export class ScheduleSingleEventsComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// TODO: replace with filter
|
// TODO: replace with filter
|
||||||
const test = ScheduleSingleEventsComponent.groupDateSeriesToDays(
|
return ScheduleSingleEventsComponent.groupDateSeriesToDays(
|
||||||
dateSeries.dates.filter(it => !it.repeatFrequency),
|
dateSeries.dates.filter(it => !it.repeatFrequency),
|
||||||
);
|
);
|
||||||
return test;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* OnDestroy
|
|
||||||
*/
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
this._uuidSubscription.unsubscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize
|
|
||||||
*/
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this._uuidSubscription = this.scheduleProvider.uuids$.subscribe(async result => {
|
this.scheduleProvider.uuids$.pipe(takeUntilDestroyed(this.destroy$)).subscribe(async result => {
|
||||||
this.uuids = result;
|
this.uuids = result;
|
||||||
this.events = this.fetchDateSeries();
|
this.events = this.fetchDateSeries();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {AfterViewInit, Component, Input, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
import {AfterViewInit, Component, Input, OnInit, ViewChild} from '@angular/core';
|
||||||
import {ActivatedRoute} from '@angular/router';
|
import {ActivatedRoute} from '@angular/router';
|
||||||
import moment, {Moment} from 'moment';
|
import moment, {Moment} from 'moment';
|
||||||
import {materialFade, materialManualFade, materialSharedAxisX} from '../../../animation/material-motion';
|
import {materialFade, materialManualFade, materialSharedAxisX} from '../../../animation/material-motion';
|
||||||
@@ -23,6 +23,7 @@ import {CalendarService} from '../../calendar/calendar.service';
|
|||||||
import {CalendarComponent} from './components/calendar.component';
|
import {CalendarComponent} from './components/calendar.component';
|
||||||
import {IonContent, IonDatetime} from '@ionic/angular';
|
import {IonContent, IonDatetime} from '@ionic/angular';
|
||||||
import {SwiperComponent} from 'swiper/angular';
|
import {SwiperComponent} from 'swiper/angular';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that displays the schedule
|
* Component that displays the schedule
|
||||||
@@ -33,10 +34,7 @@ import {SwiperComponent} from 'swiper/angular';
|
|||||||
styleUrls: ['schedule-view.scss', './components/calendar-component.scss'],
|
styleUrls: ['schedule-view.scss', './components/calendar-component.scss'],
|
||||||
animations: [materialFade, materialSharedAxisX, materialManualFade],
|
animations: [materialFade, materialSharedAxisX, materialManualFade],
|
||||||
})
|
})
|
||||||
export class ScheduleViewComponent
|
export class ScheduleViewComponent extends CalendarComponent implements OnInit, AfterViewInit {
|
||||||
extends CalendarComponent
|
|
||||||
implements OnInit, AfterViewInit, OnDestroy, AfterViewInit
|
|
||||||
{
|
|
||||||
@ViewChild('mainSwiper') mainSwiper: SwiperComponent;
|
@ViewChild('mainSwiper') mainSwiper: SwiperComponent;
|
||||||
|
|
||||||
@ViewChild('headerSwiper') headerSwiper: SwiperComponent;
|
@ViewChild('headerSwiper') headerSwiper: SwiperComponent;
|
||||||
@@ -99,11 +97,8 @@ export class ScheduleViewComponent
|
|||||||
* Initialize
|
* Initialize
|
||||||
*/
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
super.onInit();
|
super.ngOnInit();
|
||||||
if (this.calendarServiceSubscription) {
|
this.calendarService.goToDateClicked.pipe(takeUntilDestroyed(this.destroy$)).subscribe(() => {
|
||||||
this.calendarServiceSubscription.unsubscribe();
|
|
||||||
}
|
|
||||||
this.calendarServiceSubscription = this.calendarService.goToDateClicked.subscribe(() => {
|
|
||||||
this.slideToToday();
|
this.slideToToday();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -112,13 +107,6 @@ export class ScheduleViewComponent
|
|||||||
this.slideToToday();
|
this.slideToToday();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* OnDestroy
|
|
||||||
*/
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Slide today into view.
|
* Slide today into view.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 StApps
|
* Copyright (C) 2023 StApps
|
||||||
* This program is free software: you can redistribute it and/or modify it
|
* 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
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
* Software Foundation, version 3.
|
* Software Foundation, version 3.
|
||||||
@@ -12,8 +12,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
import {Injectable} from '@angular/core';
|
||||||
import {Injectable, OnDestroy} from '@angular/core';
|
|
||||||
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
|
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
|
||||||
import {
|
import {
|
||||||
SCLanguage,
|
SCLanguage,
|
||||||
@@ -24,8 +23,8 @@ import {
|
|||||||
SCTranslations,
|
SCTranslations,
|
||||||
} from '@openstapps/core';
|
} from '@openstapps/core';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {Subscription} from 'rxjs';
|
|
||||||
import {isDefined, ThingTranslateParser} from './thing-translate.parser';
|
import {isDefined, ThingTranslateParser} from './thing-translate.parser';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
// export const DEFAULT_LANGUAGE = new InjectionToken<string>('DEFAULT_LANGUAGE');
|
// export const DEFAULT_LANGUAGE = new InjectionToken<string>('DEFAULT_LANGUAGE');
|
||||||
|
|
||||||
@@ -34,9 +33,7 @@ import {isDefined, ThingTranslateParser} from './thing-translate.parser';
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class ThingTranslateService implements OnDestroy {
|
export class ThingTranslateService {
|
||||||
onLangChange: Subscription;
|
|
||||||
|
|
||||||
translator: SCThingTranslator;
|
translator: SCThingTranslator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,7 +46,7 @@ export class ThingTranslateService implements OnDestroy {
|
|||||||
(translateService.currentLang ?? translateService.defaultLang) as SCLanguageCode,
|
(translateService.currentLang ?? translateService.defaultLang) as SCLanguageCode,
|
||||||
);
|
);
|
||||||
/** set the default language from configuration */
|
/** set the default language from configuration */
|
||||||
this.onLangChange = this.translateService.onLangChange.subscribe((event: LangChangeEvent) => {
|
this.translateService.onLangChange.pipe(takeUntilDestroyed()).subscribe((event: LangChangeEvent) => {
|
||||||
this.translator.language = event.lang as keyof SCTranslations<SCLanguage>;
|
this.translator.language = event.lang as keyof SCTranslations<SCLanguage>;
|
||||||
moment.locale(event.lang);
|
moment.locale(event.lang);
|
||||||
});
|
});
|
||||||
@@ -104,10 +101,4 @@ export class ThingTranslateService implements OnDestroy {
|
|||||||
|
|
||||||
return this.getParsedResult(translatedPropertyNames, keyPath);
|
return this.getParsedResult(translatedPropertyNames, keyPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
if (!this.onLangChange.closed) {
|
|
||||||
this.onLangChange.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,17 +12,18 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Injectable, OnDestroy, Pipe, PipeTransform} from '@angular/core';
|
import {DestroyRef, inject, Injectable, Pipe, PipeTransform} from '@angular/core';
|
||||||
import {TranslateService} from '@ngx-translate/core';
|
import {TranslateService} from '@ngx-translate/core';
|
||||||
import {get} from '@openstapps/collection-utils';
|
import {get} from '@openstapps/collection-utils';
|
||||||
import {Subscription} from 'rxjs';
|
import {Subscription} from 'rxjs';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'translateSimple',
|
name: 'translateSimple',
|
||||||
pure: false,
|
pure: false,
|
||||||
})
|
})
|
||||||
export class TranslateSimplePipe implements PipeTransform, OnDestroy {
|
export class TranslateSimplePipe implements PipeTransform {
|
||||||
value: unknown;
|
value: unknown;
|
||||||
|
|
||||||
query: unknown;
|
query: unknown;
|
||||||
@@ -31,6 +32,8 @@ export class TranslateSimplePipe implements PipeTransform, OnDestroy {
|
|||||||
|
|
||||||
onLangChange: Subscription;
|
onLangChange: Subscription;
|
||||||
|
|
||||||
|
destroy$ = inject(DestroyRef);
|
||||||
|
|
||||||
constructor(private readonly translate: TranslateService) {}
|
constructor(private readonly translate: TranslateService) {}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
@@ -58,14 +61,12 @@ export class TranslateSimplePipe implements PipeTransform, OnDestroy {
|
|||||||
|
|
||||||
this.updateValue();
|
this.updateValue();
|
||||||
|
|
||||||
this.onLangChange ??= this.translate.onLangChange.subscribe(() => {
|
this.onLangChange ??= this.translate.onLangChange
|
||||||
this.updateValue();
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
});
|
.subscribe(() => {
|
||||||
|
this.updateValue();
|
||||||
|
});
|
||||||
|
|
||||||
return this.value as never;
|
return this.value as never;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
this.onLangChange?.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 StApps
|
* Copyright (C) 2023 StApps
|
||||||
* This program is free software: you can redistribute it and/or modify it
|
* 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
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
* Software Foundation, version 3.
|
* Software Foundation, version 3.
|
||||||
@@ -12,20 +12,29 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
import {
|
||||||
import {Directive, ElementRef, Host, Optional, Self, ViewContainerRef} from '@angular/core';
|
DestroyRef,
|
||||||
|
Directive,
|
||||||
|
ElementRef,
|
||||||
|
Host,
|
||||||
|
inject,
|
||||||
|
OnInit,
|
||||||
|
Optional,
|
||||||
|
Self,
|
||||||
|
ViewContainerRef,
|
||||||
|
} from '@angular/core';
|
||||||
import {SCIcon} from './icon';
|
import {SCIcon} from './icon';
|
||||||
import {IconReplacer} from './replace-util';
|
import {IconReplacer} from './replace-util';
|
||||||
import {TranslateService} from '@ngx-translate/core';
|
import {TranslateService} from '@ngx-translate/core';
|
||||||
import {Subscription} from 'rxjs';
|
|
||||||
import {IonBackButton} from '@ionic/angular';
|
import {IonBackButton} from '@ionic/angular';
|
||||||
import {TitleCasePipe} from '@angular/common';
|
import {TitleCasePipe} from '@angular/common';
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: 'ion-back-button',
|
selector: 'ion-back-button',
|
||||||
})
|
})
|
||||||
export class IonBackButtonDirective extends IconReplacer {
|
export class IonBackButtonDirective extends IconReplacer implements OnInit {
|
||||||
private subscriptions: Subscription[] = [];
|
destroy$ = inject(DestroyRef);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
element: ElementRef,
|
element: ElementRef,
|
||||||
@@ -45,17 +54,13 @@ export class IonBackButtonDirective extends IconReplacer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
async ngOnInit() {
|
||||||
this.subscriptions.push(
|
await super.ngOnInit();
|
||||||
this.translateService.stream('back').subscribe((value: string) => {
|
this.translateService
|
||||||
|
.stream('back')
|
||||||
|
.pipe(takeUntilDestroyed(this.destroy$))
|
||||||
|
.subscribe((value: string) => {
|
||||||
this.ionBackButton.text = this.titleCasePipe.transform(value);
|
this.ionBackButton.text = this.titleCasePipe.transform(value);
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
for (const subscription of this.subscriptions) {
|
|
||||||
subscription.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Directive, ElementRef, ViewContainerRef} from '@angular/core';
|
import {Directive, ElementRef, ViewContainerRef} from '@angular/core';
|
||||||
import {SCIcon} from './icon';
|
import {SCIcon} from './icon';
|
||||||
import {IconReplacer} from './replace-util';
|
import {IconReplacer} from './replace-util';
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Directive, ElementRef, ViewContainerRef} from '@angular/core';
|
import {Directive, ElementRef, ViewContainerRef} from '@angular/core';
|
||||||
import {SCIcon} from './icon';
|
import {SCIcon} from './icon';
|
||||||
import {IconReplacer} from './replace-util';
|
import {IconReplacer} from './replace-util';
|
||||||
|
|||||||
@@ -65,24 +65,7 @@ export abstract class IconReplacer implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
abstract replace(): void;
|
abstract replace(): void;
|
||||||
|
|
||||||
/**
|
|
||||||
* If any additional work needs to be done, this
|
|
||||||
* is called during ngOnInit
|
|
||||||
*/
|
|
||||||
init() {
|
|
||||||
// noop
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If you need to do cleanup, this method is called during ngOnDestroy
|
|
||||||
*/
|
|
||||||
destroy() {
|
|
||||||
// noop
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.init();
|
|
||||||
|
|
||||||
if (this.host) {
|
if (this.host) {
|
||||||
this.attachObserver();
|
this.attachObserver();
|
||||||
} else {
|
} else {
|
||||||
@@ -110,8 +93,8 @@ export abstract class IconReplacer implements OnInit, OnDestroy {
|
|||||||
scIcon.location.nativeElement.classList.add(...icon.classList);
|
scIcon.location.nativeElement.classList.add(...icon.classList);
|
||||||
|
|
||||||
if (this.iconDomLocation === 'shadow') {
|
if (this.iconDomLocation === 'shadow') {
|
||||||
// shadow dom needs to utilize slotting, to put it outside
|
// shadow dom needs to utilize slotting, to put it outside the shadow dom
|
||||||
// the shadow dom, otherwise it won't receive any css data
|
// otherwise it won't receive any css data
|
||||||
const slot = document.createElement('slot');
|
const slot = document.createElement('slot');
|
||||||
slot.name = this.slotName + slotName;
|
slot.name = this.slotName + slotName;
|
||||||
icon.replaceWith(slot);
|
icon.replaceWith(slot);
|
||||||
@@ -137,6 +120,5 @@ export abstract class IconReplacer implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.mutationObserver?.disconnect();
|
this.mutationObserver?.disconnect();
|
||||||
this.destroy();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
frontend/app/src/app/util/pause-when.ts
Normal file
12
frontend/app/src/app/util/pause-when.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import {filter, MonoTypeOperatorFunction, Observable, repeat, takeUntil} from 'rxjs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pause the observable if the notifier emits true, and resume when it emits false
|
||||||
|
*/
|
||||||
|
export function pauseWhen<T>(notifier: Observable<boolean>): MonoTypeOperatorFunction<T> {
|
||||||
|
return value =>
|
||||||
|
value.pipe(
|
||||||
|
takeUntil(notifier.pipe(filter(it => it))),
|
||||||
|
repeat({delay: () => notifier.pipe(filter(it => !it))}),
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user