mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-19 16:13:06 +00:00
feat: add not found screen
This commit is contained in:
@@ -16,6 +16,7 @@ import {CommonModule} from '@angular/common';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {Network} from '@ionic-native/network/ngx';
|
||||
import {IonicModule} from '@ionic/angular';
|
||||
import {TranslateModule} from '@ngx-translate/core';
|
||||
import {MarkdownModule} from 'ngx-markdown';
|
||||
@@ -135,6 +136,7 @@ import {VideoListItem} from './types/video/video-list-item.component';
|
||||
providers: [
|
||||
DataProvider,
|
||||
DataFacetsProvider,
|
||||
Network,
|
||||
StAppsWebHttpClient,
|
||||
],
|
||||
})
|
||||
|
||||
@@ -12,43 +12,53 @@
|
||||
* 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} from '@angular/core';
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {Network} from '@ionic-native/network/ngx';
|
||||
import {IonRefresher} from '@ionic/angular';
|
||||
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
|
||||
import {SCLanguageCode, SCThing, SCUuid} from '@openstapps/core';
|
||||
import {SCLanguageCode, SCSaveableThing, SCThings, SCUuid} from '@openstapps/core';
|
||||
import {DataProvider, DataScope} from '../data.provider';
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* A Component to display an SCThing detailed
|
||||
*/
|
||||
@Component({
|
||||
selector: 'stapps-data-detail',
|
||||
styleUrls: ['data-detail.scss'],
|
||||
templateUrl: 'data-detail.html',
|
||||
})
|
||||
export class DataDetailComponent {
|
||||
export class DataDetailComponent implements OnInit {
|
||||
/**
|
||||
* TODO
|
||||
* The associated item
|
||||
*
|
||||
* undefined if not loaded, null when unavailable
|
||||
*/
|
||||
dataProvider: DataProvider;
|
||||
item?: SCThings | null = undefined;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
item: SCThing;
|
||||
/**
|
||||
* TODO
|
||||
* The language of the item
|
||||
*/
|
||||
language: SCLanguageCode;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param route TODO
|
||||
* @param dataProvider TODO
|
||||
* @param translateService TODO
|
||||
* Type guard for SCSavableThing
|
||||
*/
|
||||
constructor(private readonly route: ActivatedRoute, dataProvider: DataProvider, translateService: TranslateService) {
|
||||
this.dataProvider = dataProvider;
|
||||
static isSCSavableThing(thing: SCThings | SCSaveableThing<SCThings>): thing is SCSaveableThing<SCThings> {
|
||||
return typeof (thing as SCSaveableThing<SCThings>).data !== 'undefined';
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param route the route the page was accessed from
|
||||
* @param dataProvider the data provider
|
||||
* @param network the network provider
|
||||
* @param translateService the translation service
|
||||
*/
|
||||
constructor(private readonly route: ActivatedRoute,
|
||||
private readonly dataProvider: DataProvider,
|
||||
private readonly network: Network,
|
||||
translateService: TranslateService) {
|
||||
this.language = translateService.currentLang as SCLanguageCode;
|
||||
translateService.onLangChange.subscribe((event: LangChangeEvent) => {
|
||||
this.language = event.lang as SCLanguageCode;
|
||||
@@ -60,19 +70,27 @@ export class DataDetailComponent {
|
||||
*
|
||||
* @param uid Unique identifier of a thing
|
||||
*/
|
||||
async getItem(uid: SCUuid): Promise<void> {
|
||||
await this.dataProvider.get(uid, DataScope.Remote)
|
||||
.then((data) => {
|
||||
this.item = data;
|
||||
});
|
||||
async getItem(uid: SCUuid) {
|
||||
try {
|
||||
const item = await this.dataProvider.get(uid, DataScope.Remote);
|
||||
this.item = DataDetailComponent.isSCSavableThing(item) ? item.data : item;
|
||||
} catch (_) {
|
||||
this.item = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* Check if we have internet
|
||||
*/
|
||||
isDisconnected(): boolean {
|
||||
return this.network.type === this.network.Connection.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize
|
||||
*/
|
||||
// tslint:disable-next-line:prefer-function-over-method
|
||||
ngOnInit() {
|
||||
this.getItem(this.route.snapshot.paramMap.get('uid') || '');
|
||||
void this.getItem(this.route.snapshot.paramMap.get('uid') ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,7 +99,7 @@ export class DataDetailComponent {
|
||||
* @param refresher Refresher component that triggers the update
|
||||
*/
|
||||
async refresh(refresher: IonRefresher) {
|
||||
await this.getItem(this.item.uid);
|
||||
refresher.complete();
|
||||
await this.getItem(this.item?.uid ?? this.route.snapshot.paramMap.get('uid') ?? '');
|
||||
await refresher.complete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,32 @@
|
||||
refreshingText="{{'data.REFRESHING' | translate}}">
|
||||
</ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<ng-container *ngIf="!item">
|
||||
<stapps-skeleton-list-item></stapps-skeleton-list-item>
|
||||
<stapps-skeleton-simple-card></stapps-skeleton-simple-card>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="item">
|
||||
<stapps-data-list-item [item]="item"></stapps-data-list-item>
|
||||
<stapps-data-detail-content [item]="item"></stapps-data-detail-content>
|
||||
</ng-container>
|
||||
<div [ngSwitch]="true">
|
||||
<ng-container *ngSwitchCase='!item && isDisconnected()'>
|
||||
<div class='notFoundContainer'>
|
||||
<ion-icon name='no-connection'>
|
||||
</ion-icon>
|
||||
<ion-label>
|
||||
{{ 'data.detail.COULD_NOT_CONNECT' | translate }}
|
||||
</ion-label>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="item === null">
|
||||
<div class="notFoundContainer">
|
||||
<ion-icon name="broken-link">
|
||||
</ion-icon>
|
||||
<ion-label>
|
||||
{{ 'data.detail.NOT_FOUND' | translate }}
|
||||
</ion-label>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="!item && item !== null">
|
||||
<stapps-skeleton-list-item></stapps-skeleton-list-item>
|
||||
<stapps-skeleton-simple-card></stapps-skeleton-simple-card>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchDefault>
|
||||
<stapps-data-list-item [item]="item"></stapps-data-list-item>
|
||||
<stapps-data-detail-content [item]="item"></stapps-data-detail-content>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ion-content>
|
||||
|
||||
@@ -11,3 +11,20 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notFoundContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
min-height: 50vh;
|
||||
|
||||
ion-icon {
|
||||
font-size: 64px;
|
||||
}
|
||||
|
||||
ion-label {
|
||||
font-size: x-large;
|
||||
}
|
||||
}
|
||||
|
||||
27
src/assets/custom-ion-icons/broken-link.svg
Normal file
27
src/assets/custom-ion-icons/broken-link.svg
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 500 500" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;">
|
||||
<g transform="matrix(1,0,0,1,-600,0)">
|
||||
<rect id="Artboard2" x="600" y="0" width="500" height="500" style="fill:none;"/>
|
||||
<g id="Artboard21" serif:id="Artboard2">
|
||||
<g transform="matrix(0.448766,-0.893649,0.893649,0.448766,469.063,342.066)">
|
||||
<path d="M172.406,353.651L144,352C91.336,352 48,308.664 48,256C48,203.336 91.336,160 144,160L208,160" style="fill:none;fill-rule:nonzero;stroke:currentColor;stroke-width:36px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.998306,-0.0581818,0.0581818,0.998306,578.658,-36.4373)">
|
||||
<path d="M304,160L368,160C420.664,160 464,203.336 464,256C464,308.664 420.664,352 368,352L344.108,351.046" style="fill:none;fill-rule:nonzero;stroke:currentColor;stroke-width:36px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.835467,-0.54954,0.54954,0.835467,485.54,168.526)">
|
||||
<path d="M313.991,245.837L367.731,276.894" style="fill:none;fill-rule:nonzero;stroke:currentColor;stroke-width:36px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.835467,-0.54954,-0.54954,-0.835467,642.903,689.819)">
|
||||
<path d="M308.423,242.423L368.097,273.154" style="fill:none;fill-rule:nonzero;stroke:currentColor;stroke-width:36px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.835467,-0.54954,-0.54954,-0.835467,611.794,572.634)">
|
||||
<path d="M343.592,241L328.795,281" style="fill:none;fill-rule:nonzero;stroke:currentColor;stroke-width:36px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.835467,-0.54954,0.54954,0.835467,397.953,88.4905)">
|
||||
<path d="M343.592,241L328.795,281" style="fill:none;fill-rule:nonzero;stroke:currentColor;stroke-width:36px;"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
13
src/assets/custom-ion-icons/no-connection.svg
Normal file
13
src/assets/custom-ion-icons/no-connection.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-miterlimit:10;">
|
||||
<path d="M74.216,154.839C57.516,184.784 48,219.281 48,256C48,370.87 141.13,464 256,464C293.515,464 328.711,454.067 359.102,436.688" style="fill:none;fill-rule:nonzero;stroke:black;stroke-width:32px;"/>
|
||||
<path d="M437.104,358.371C454.225,328.147 464,293.215 464,256C464,141.13 370.87,48 256,48C218.643,48 183.586,57.849 153.283,75.093" style="fill:none;fill-rule:nonzero;stroke:black;stroke-width:32px;"/>
|
||||
<path d="M367.788,281.198C368.369,272.938 368.67,264.529 368.67,256C368.67,141.13 314.07,48 256,48C229.248,48 203.232,67.766 182.951,100.317" style="fill:none;fill-rule:nonzero;stroke:black;stroke-width:32px;"/>
|
||||
<path d="M144.485,227.192C143.725,236.608 143.33,246.225 143.33,256C143.33,370.87 197.93,464 256,464C282.923,464 309.099,443.982 329.435,411.06" style="fill:none;fill-rule:nonzero;stroke:black;stroke-width:32px;"/>
|
||||
<path d="M238.57,160.066C244.322,160.466 250.135,160.67 256,160.67C308.29,160.67 356.43,144.48 394.67,117.33M276.68,352.181C269.87,351.618 262.972,351.33 256,351.33C203.71,351.33 155.57,367.52 117.33,394.67" style="fill:none;fill-rule:nonzero;stroke:black;stroke-width:32px;stroke-linejoin:round;stroke-miterlimit:4;"/>
|
||||
<path d="M256,48L256,176.215M464,256L332.18,256" style="fill:none;fill-rule:nonzero;stroke:black;stroke-width:32px;"/>
|
||||
<path d="M256,335.311L256,464" style="fill:none;fill-rule:nonzero;stroke:black;stroke-width:32px;"/>
|
||||
<path d="M180.214,256L48,256" style="fill:none;fill-rule:nonzero;stroke:black;stroke-width:32px;"/>
|
||||
<path d="M448,448L64,64" style="fill:none;fill-rule:nonzero;stroke:black;stroke-width:32px;"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -3,7 +3,9 @@
|
||||
"REFRESH_ACTION": "Aktualisieren",
|
||||
"REFRESHING": "Aktualisierung läuft...",
|
||||
"detail": {
|
||||
"TITLE": "Detailansicht"
|
||||
"TITLE": "Detailansicht",
|
||||
"NOT_FOUND": "Nicht gefunden",
|
||||
"COULD_NOT_CONNECT": "Verbindung fehlgeschlagen"
|
||||
},
|
||||
"types": {
|
||||
"dish": {
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
"REFRESH_ACTION": "Refresh",
|
||||
"REFRESHING": "Refreshing...",
|
||||
"detail": {
|
||||
"TITLE": "Details"
|
||||
"TITLE": "Details",
|
||||
"NOT_FOUND": "Not found",
|
||||
"COULD_NOT_CONNECT": "Couldn't connect"
|
||||
},
|
||||
"types": {
|
||||
"dish": {
|
||||
|
||||
Reference in New Issue
Block a user