mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-10 19:52:53 +00:00
committed by
Sebastian Lange
parent
88f87a2ce1
commit
e1039aa226
@@ -17,7 +17,7 @@ import {HTTP_INTERCEPTORS, HttpClient,
|
|||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import {SCIndexResponse, SCThingType} from '@openstapps/core';
|
import {SCIndexResponse, SCThingType} from '@openstapps/core';
|
||||||
import {Observable, of} from 'rxjs';
|
import {Observable, of} from 'rxjs';
|
||||||
import {map} from 'rxjs/operators';
|
import {map, delay} from 'rxjs/operators';
|
||||||
import {SampleThings} from './data/sample-things';
|
import {SampleThings} from './data/sample-things';
|
||||||
|
|
||||||
const sampleIndexResponse: SCIndexResponse = {
|
const sampleIndexResponse: SCIndexResponse = {
|
||||||
@@ -150,12 +150,12 @@ export class FakeBackendInterceptor implements HttpInterceptor {
|
|||||||
return this.sampleFetcher.getSampleThing(request.body.filter.arguments.value)
|
return this.sampleFetcher.getSampleThing(request.body.filter.arguments.value)
|
||||||
.pipe(map((sampleData: any) => {
|
.pipe(map((sampleData: any) => {
|
||||||
return new HttpResponse({status: 200, body: {data: sampleData}});
|
return new HttpResponse({status: 200, body: {data: sampleData}});
|
||||||
}));
|
}), delay(1000)); // add delay for skeleton screens to be seen (see !16)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.sampleFetcher.getSampleThings().pipe(map((sampleData: any) => {
|
return this.sampleFetcher.getSampleThings().pipe(map((sampleData: any) => {
|
||||||
return new HttpResponse({status: 200, body: {data: sampleData}});
|
return new HttpResponse({status: 200, body: {data: sampleData}});
|
||||||
}));
|
}), delay(1000)); // add delay for skeleton screens to be seen (see !16)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return next.handle(request);
|
return next.handle(request);
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ import {SemesterDetailContentComponent} from './types/semester/semester-detail-c
|
|||||||
import {SemesterListItem} from './types/semester/semester-list-item.component';
|
import {SemesterListItem} from './types/semester/semester-list-item.component';
|
||||||
import {VideoDetailContentComponent} from './types/video/video-detail-content.component';
|
import {VideoDetailContentComponent} from './types/video/video-detail-content.component';
|
||||||
import {VideoListItem} from './types/video/video-list-item.component';
|
import {VideoListItem} from './types/video/video-list-item.component';
|
||||||
|
import {SkeletonListItem} from './elements/skeleton-list-item.component';
|
||||||
|
import {SkeletonSimpleCard} from './elements/skeleton-simple-card.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@@ -69,6 +71,7 @@ import {VideoListItem} from './types/video/video-list-item.component';
|
|||||||
ArticleDetailContentComponent,
|
ArticleDetailContentComponent,
|
||||||
ArticleListItem,
|
ArticleListItem,
|
||||||
SimpleCardComponent,
|
SimpleCardComponent,
|
||||||
|
SkeletonSimpleCard,
|
||||||
CatalogDetailContentComponent,
|
CatalogDetailContentComponent,
|
||||||
CatalogListItem,
|
CatalogListItem,
|
||||||
DataDetailComponent,
|
DataDetailComponent,
|
||||||
@@ -96,6 +99,7 @@ import {VideoListItem} from './types/video/video-list-item.component';
|
|||||||
PlaceListItem,
|
PlaceListItem,
|
||||||
SemesterDetailContentComponent,
|
SemesterDetailContentComponent,
|
||||||
SemesterListItem,
|
SemesterListItem,
|
||||||
|
SkeletonListItem,
|
||||||
VideoDetailContentComponent,
|
VideoDetailContentComponent,
|
||||||
VideoListItem,
|
VideoListItem,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -28,36 +28,37 @@ export class DataDetailComponent {
|
|||||||
dataProvider: DataProvider;
|
dataProvider: DataProvider;
|
||||||
item: SCThing;
|
item: SCThing;
|
||||||
language: SCLanguageCode;
|
language: SCLanguageCode;
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, dataProvider: DataProvider, translateService: TranslateService) {
|
constructor(private route: ActivatedRoute, dataProvider: DataProvider, translateService: TranslateService) {
|
||||||
this.dataProvider = dataProvider;
|
this.dataProvider = dataProvider;
|
||||||
this.language = translateService.currentLang as SCLanguageCode;
|
this.language = translateService.currentLang as SCLanguageCode;
|
||||||
|
|
||||||
// alert(translateService.currentLang);
|
|
||||||
|
|
||||||
translateService.onLangChange.subscribe((event: LangChangeEvent) => {
|
translateService.onLangChange.subscribe((event: LangChangeEvent) => {
|
||||||
this.language = event.lang as SCLanguageCode;
|
this.language = event.lang as SCLanguageCode;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides data item with given UID
|
* Provides data item with given UID
|
||||||
*
|
*
|
||||||
* @param uid Unique identifier of a thing
|
* @param uid Unique identifier of a thing
|
||||||
*/
|
*/
|
||||||
async getItem(uid: SCUuid): Promise<SCThing> {
|
async getItem(uid: SCUuid): Promise<void> {
|
||||||
return (await this.dataProvider.get(uid, DataScope.Remote));
|
this.dataProvider.get(uid, DataScope.Remote).then((data) => {
|
||||||
|
this.item = data;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
ngOnInit() {
|
||||||
this.item = await this.getItem(this.route.snapshot.paramMap.get('uid') || '');
|
this.getItem(this.route.snapshot.paramMap.get('uid') || '');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the shown thing
|
* Updates the shown thing
|
||||||
*
|
*
|
||||||
* @param refresher Refresher component the triggers the update
|
* @param refresher Refresher component that triggers the update
|
||||||
*/
|
*/
|
||||||
async refresh(refresher: IonRefresher) {
|
async refresh(refresher: IonRefresher) {
|
||||||
this.item = await this.getItem(this.item.uid);
|
await this.getItem(this.item.uid);
|
||||||
refresher.complete();
|
refresher.complete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,18 @@
|
|||||||
<ion-title text-center>{{'data.detail.TITLE' | translate}}</ion-title>
|
<ion-title text-center>{{'data.detail.TITLE' | translate}}</ion-title>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
<ion-content padding *ngIf="item">
|
<ion-content padding>
|
||||||
<ion-refresher slot="fixed" (ionRefresh)="refresh($event.target)">
|
<ion-refresher slot="fixed" (ionRefresh)="refresh($event.target)">
|
||||||
<ion-refresher-content pullingIcon="arrow-dropdown" pullingText="{{'data.REFRESH_ACTION' | translate}}"
|
<ion-refresher-content pullingIcon="arrow-dropdown" pullingText="{{'data.REFRESH_ACTION' | translate}}"
|
||||||
refreshingText="{{'data.REFRESHING' | translate}}">
|
refreshingText="{{'data.REFRESHING' | translate}}">
|
||||||
</ion-refresher-content>
|
</ion-refresher-content>
|
||||||
</ion-refresher>
|
</ion-refresher>
|
||||||
<stapps-data-list-item [item]="item"></stapps-data-list-item>
|
<ng-container *ngIf="!item">
|
||||||
<stapps-data-detail-content [item]="item"></stapps-data-detail-content>
|
<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>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 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} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'stapps-skeleton-list-item',
|
||||||
|
templateUrl: 'skeleton-list-item.html',
|
||||||
|
})
|
||||||
|
export class SkeletonListItem {
|
||||||
|
}
|
||||||
14
src/app/modules/data/elements/skeleton-list-item.html
Normal file
14
src/app/modules/data/elements/skeleton-list-item.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<ion-item>
|
||||||
|
<ion-thumbnail slot="start">
|
||||||
|
<ion-skeleton-text animated></ion-skeleton-text>
|
||||||
|
</ion-thumbnail>
|
||||||
|
<ion-grid>
|
||||||
|
<ion-row>
|
||||||
|
<ion-col>
|
||||||
|
<h2 class="name"><ion-skeleton-text animated style="width: 80%"></ion-skeleton-text></h2>
|
||||||
|
<p><ion-skeleton-text animated style="width: 80%;"></ion-skeleton-text></p>
|
||||||
|
<ion-note><ion-skeleton-text animated style="width: 20%"></ion-skeleton-text></ion-note>
|
||||||
|
</ion-col>
|
||||||
|
</ion-row>
|
||||||
|
</ion-grid>
|
||||||
|
</ion-item>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 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} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'stapps-skeleton-simple-card',
|
||||||
|
templateUrl: 'skeleton-simple-card.html',
|
||||||
|
})
|
||||||
|
export class SkeletonSimpleCard {
|
||||||
|
}
|
||||||
8
src/app/modules/data/elements/skeleton-simple-card.html
Normal file
8
src/app/modules/data/elements/skeleton-simple-card.html
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<ion-card>
|
||||||
|
<ion-card-header>
|
||||||
|
<ion-skeleton-text animated style="width: 15%"></ion-skeleton-text>
|
||||||
|
</ion-card-header>
|
||||||
|
<ion-card-content>
|
||||||
|
<p><ion-skeleton-text animated style="width: 85%;"></ion-skeleton-text></p>
|
||||||
|
</ion-card-content>
|
||||||
|
</ion-card>
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
import {AlertController, LoadingController} from '@ionic/angular';
|
import {AlertController} from '@ionic/angular';
|
||||||
import {SCThing} from '@openstapps/core';
|
import {SCThing} from '@openstapps/core';
|
||||||
import {Subject} from 'rxjs';
|
import {Subject} from 'rxjs';
|
||||||
import {debounceTime, distinctUntilChanged} from 'rxjs/operators';
|
import {debounceTime, distinctUntilChanged} from 'rxjs/operators';
|
||||||
@@ -25,8 +25,9 @@ import {DataProvider} from '../data.provider';
|
|||||||
})
|
})
|
||||||
export class DataListComponent {
|
export class DataListComponent {
|
||||||
dataProvider: DataProvider;
|
dataProvider: DataProvider;
|
||||||
items: SCThing[] = [];
|
items: SCThing[];
|
||||||
selectedItem: any;
|
selectedItem: any;
|
||||||
|
loaded: boolean = false;
|
||||||
|
|
||||||
size: number = 30;
|
size: number = 30;
|
||||||
from: number = 0;
|
from: number = 0;
|
||||||
@@ -34,10 +35,7 @@ export class DataListComponent {
|
|||||||
query: string;
|
query: string;
|
||||||
queryChanged: Subject<string> = new Subject<string>();
|
queryChanged: Subject<string> = new Subject<string>();
|
||||||
|
|
||||||
loading: HTMLIonLoadingElement;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private loadingController: LoadingController,
|
|
||||||
private alertController: AlertController,
|
private alertController: AlertController,
|
||||||
dataProvider: DataProvider,
|
dataProvider: DataProvider,
|
||||||
) {
|
) {
|
||||||
@@ -49,31 +47,19 @@ export class DataListComponent {
|
|||||||
.subscribe((model) => {
|
.subscribe((model) => {
|
||||||
this.from = 0;
|
this.from = 0;
|
||||||
this.query = model;
|
this.query = model;
|
||||||
this.items = [];
|
|
||||||
this.fetchItems();
|
this.fetchItems();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.fetchItems();
|
this.fetchItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async fetchItems(): Promise<any> {
|
private async fetchItems(): Promise<any> {
|
||||||
if (this.from === 0) {
|
|
||||||
this.loading = await this.loadingController.create();
|
|
||||||
await this.loading.present();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.dataProvider.search({
|
return this.dataProvider.search({
|
||||||
from: this.from,
|
from: this.from,
|
||||||
query: this.query,
|
query: this.query,
|
||||||
size: this.size,
|
size: this.size,
|
||||||
} as any).then((result) => {
|
} as any).then((result) => {
|
||||||
result.data.forEach((item) => {
|
this.items = result.data;
|
||||||
this.items.push(item);
|
this.loaded = true;
|
||||||
});
|
|
||||||
|
|
||||||
if (this.from === 0) {
|
|
||||||
this.loading.dismiss();
|
|
||||||
}
|
|
||||||
}, async (err) => {
|
}, async (err) => {
|
||||||
const alert: HTMLIonAlertElement = await this.alertController.create({
|
const alert: HTMLIonAlertElement = await this.alertController.create({
|
||||||
buttons: ['Dismiss'],
|
buttons: ['Dismiss'],
|
||||||
@@ -82,8 +68,6 @@ export class DataListComponent {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await alert.present();
|
await alert.present();
|
||||||
|
|
||||||
await this.loading.dismiss();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,10 +12,12 @@
|
|||||||
</ion-header>
|
</ion-header>
|
||||||
|
|
||||||
<ion-content>
|
<ion-content>
|
||||||
<ion-list *ngFor="let item of items">
|
<ion-list *ngIf="items">
|
||||||
<stapps-data-list-item [item]="item"></stapps-data-list-item>
|
<stapps-data-list-item [item]="item" *ngFor="let item of items"></stapps-data-list-item>
|
||||||
|
</ion-list>
|
||||||
|
<ion-list *ngIf="!items">
|
||||||
|
<stapps-skeleton-list-item *ngFor="let skeleton of [1, 2, 3, 4, 5]"></stapps-skeleton-list-item>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
|
|
||||||
<ion-infinite-scroll (ionInfinite)="loadMore($event)">
|
<ion-infinite-scroll (ionInfinite)="loadMore($event)">
|
||||||
<ion-infinite-scroll-content></ion-infinite-scroll-content>
|
<ion-infinite-scroll-content></ion-infinite-scroll-content>
|
||||||
</ion-infinite-scroll>
|
</ion-infinite-scroll>
|
||||||
|
|||||||
Reference in New Issue
Block a user