Files
openstapps/frontend/app/src/app/modules/data/detail/data-detail.component.ts
2023-12-19 14:16:38 +01:00

158 lines
4.9 KiB
TypeScript

/*
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Component, ContentChild, EventEmitter, Input, OnInit, Output, TemplateRef} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {ModalController} from '@ionic/angular';
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
import {SCLanguageCode, SCSaveableThing, SCThings, SCUuid} from '@openstapps/core';
import {DataProvider, DataScope} from '../data.provider';
import {FavoritesService} from '../../favorites/favorites.service';
import {take} from 'rxjs/operators';
import {Network} from '@capacitor/network';
import {DataListContext} from '../list/data-list.component';
export interface ExternalDataLoadEvent {
uid: SCUuid;
forceReload: boolean;
resolve: (item: SCThings | null | undefined) => void;
}
/**
* A Component to display an SCThing detailed
*/
@Component({
selector: 'stapps-data-detail',
styleUrls: ['data-detail.scss'],
templateUrl: 'data-detail.html',
})
export class DataDetailComponent implements OnInit {
/**
* The associated item
*
* undefined if not loaded, null when unavailable
*/
item?: SCThings | null = undefined;
collapse = false;
@Input() inputItem?: SCThings;
@Input() isModal = false;
@Input() autoRouteDataPath = true;
/**
* The language of the item
*/
language: SCLanguageCode;
/**
* Indicating wether internet connectivity is given or not
*/
isDisconnected: Promise<boolean>;
@ContentChild(TemplateRef) contentTemplateRef: TemplateRef<DataListContext<SCThings>>;
@Input() externalData = false;
/**
* This is kind of a stupid situation where we would
* like to use the default header in overriding elements
* such as the assessment detail page, however the ionic
* back button will not work if the header is in a subcomponent
* which then means we have to copy and paste the header from
* here into the overriding component.
*/
@Input() defaultHeader = true;
@Output() loadItem: EventEmitter<ExternalDataLoadEvent> = new EventEmitter<ExternalDataLoadEvent>();
/**
* Type guard for SCSavableThing
*/
static isSCSavableThing(thing: SCThings | SCSaveableThing): thing is SCSaveableThing {
return (thing as SCSaveableThing).data !== undefined;
}
constructor(
protected readonly route: ActivatedRoute,
router: Router,
private readonly dataProvider: DataProvider,
private readonly favoritesService: FavoritesService,
readonly modalController: ModalController,
translateService: TranslateService,
) {
this.inputItem = router.getCurrentNavigation()?.extras.state?.item;
if (!this.inputItem?.origin) {
// We received a ThingWithoutReferences.
// This can happen, for example, when detail views use `inPlace` list items
delete this.inputItem;
}
this.language = translateService.currentLang as SCLanguageCode;
translateService.onLangChange.subscribe((event: LangChangeEvent) => {
this.language = event.lang as SCLanguageCode;
});
this.isDisconnected = new Promise(async resolve => {
const isConnected = (await Network.getStatus()).connected;
resolve(!isConnected);
});
}
/**
* Provides data item with given UID
* @param uid Unique identifier of a thing
* @param forceReload Indicating whether cached data should be ignored
*/
async getItem(uid: SCUuid, forceReload: boolean) {
try {
const item =
this.inputItem ??
(await (this.externalData
? new Promise<SCThings | null | undefined>(resolve =>
this.loadItem.emit({uid, forceReload, resolve}),
)
: this.dataProvider.get(uid, DataScope.Remote)));
this.item = item
? // eslint-disable-next-line unicorn/no-null
DataDetailComponent.isSCSavableThing(item)
? item.data
: item
: // eslint-disable-next-line unicorn/no-null
null;
} catch {
// eslint-disable-next-line unicorn/no-null
this.item = null;
}
}
async ngOnInit() {
const uid = this.route.snapshot.paramMap.get('uid') || '';
await this.getItem(uid ?? '', false);
// fallback to the saved item (from favorites)
if (this.item === null) {
this.favoritesService
.get(uid)
.pipe(take(1))
.subscribe(item => {
if (item !== undefined) {
this.item = item.data;
}
});
}
}
}