mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-22 09:32:41 +00:00
@@ -24,7 +24,7 @@
|
|||||||
</ion-refresher>
|
</ion-refresher>
|
||||||
<div [ngSwitch]="true">
|
<div [ngSwitch]="true">
|
||||||
<ng-container *ngSwitchCase="!item && isDisconnected()">
|
<ng-container *ngSwitchCase="!item && isDisconnected()">
|
||||||
<div class="notFoundContainer">
|
<div class="centeredMessageContainer">
|
||||||
<ion-icon name="no-connection"> </ion-icon>
|
<ion-icon name="no-connection"> </ion-icon>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
{{ 'data.detail.COULD_NOT_CONNECT' | translate }}
|
{{ 'data.detail.COULD_NOT_CONNECT' | translate }}
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngSwitchCase="item === null">
|
<ng-container *ngSwitchCase="item === null">
|
||||||
<div class="notFoundContainer">
|
<div class="centeredMessageContainer">
|
||||||
<ion-icon name="broken-link"> </ion-icon>
|
<ion-icon name="broken-link"> </ion-icon>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
{{ 'data.detail.NOT_FOUND' | translate }}
|
{{ 'data.detail.NOT_FOUND' | translate }}
|
||||||
|
|||||||
@@ -80,6 +80,11 @@ export class DataListComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
|
|
||||||
@ViewChild(CdkVirtualScrollViewport) viewPort: CdkVirtualScrollViewport;
|
@ViewChild(CdkVirtualScrollViewport) viewPort: CdkVirtualScrollViewport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signalizes that the data is being loaded
|
||||||
|
*/
|
||||||
|
@Input() loading = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate how many items would fill the screen
|
* Calculate how many items would fill the screen
|
||||||
*/
|
*/
|
||||||
@@ -130,10 +135,10 @@ export class DataListComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
/**
|
/**
|
||||||
* Function to call whenever scroll view visible range changed
|
* Function to call whenever scroll view visible range changed
|
||||||
*/
|
*/
|
||||||
scrolled(_index: number) {
|
scrolled(index: number) {
|
||||||
if (
|
if (
|
||||||
// first condition prevents "load more" to be executed before event having initial items
|
// first condition prevents "load more" to be executed even before scrolling
|
||||||
this.items &&
|
index > 0 &&
|
||||||
(this.items?.length ?? 0) - this.viewPort.getRenderedRange().end <=
|
(this.items?.length ?? 0) - this.viewPort.getRenderedRange().end <=
|
||||||
(this.items?.length ?? 0) * this.reloadThreshold
|
(this.items?.length ?? 0) * this.reloadThreshold
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -15,12 +15,14 @@
|
|||||||
</ion-list>
|
</ion-list>
|
||||||
</cdk-virtual-scroll-viewport>
|
</cdk-virtual-scroll-viewport>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<div [style.display]="items && items.length === 0 ? 'block' : 'none'">
|
<div
|
||||||
<ion-label class="notFoundContainer">
|
[style.display]="!loading && items && items.length === 0 ? 'block' : 'none'"
|
||||||
|
>
|
||||||
|
<ion-label class="centeredMessageContainer">
|
||||||
{{ 'search.nothing_found' | translate | titlecase }}
|
{{ 'search.nothing_found' | translate | titlecase }}
|
||||||
</ion-label>
|
</ion-label>
|
||||||
</div>
|
</div>
|
||||||
<ion-list [style.display]="items ? 'none' : 'block'">
|
<ion-list [style.display]="loading ? 'block' : 'none'">
|
||||||
<stapps-skeleton-list-item
|
<stapps-skeleton-list-item
|
||||||
[hideThumbnail]="singleType"
|
[hideThumbnail]="singleType"
|
||||||
*ngFor="let skeleton of [].constructor(skeletonItems)"
|
*ngFor="let skeleton of [].constructor(skeletonItems)"
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ export class FoodDataListComponent extends SearchPageComponent {
|
|||||||
* Sets the forced filter to present only places for eating/drinking
|
* Sets the forced filter to present only places for eating/drinking
|
||||||
*/
|
*/
|
||||||
initialize() {
|
initialize() {
|
||||||
|
this.showDefaultData = true;
|
||||||
|
|
||||||
if (this.positionService.position) {
|
if (this.positionService.position) {
|
||||||
this.sortQuery = [
|
this.sortQuery = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -40,6 +40,16 @@ import {PositionService} from '../../map/position.service';
|
|||||||
providers: [ContextMenuService],
|
providers: [ContextMenuService],
|
||||||
})
|
})
|
||||||
export class SearchPageComponent implements OnInit, OnDestroy {
|
export class SearchPageComponent implements OnInit, OnDestroy {
|
||||||
|
/**
|
||||||
|
* Signalizes that the data is being loaded
|
||||||
|
*/
|
||||||
|
loading = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show default data (e.g. when there is user interaction)
|
||||||
|
*/
|
||||||
|
@Input() showDefaultData = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Api query filter
|
* Api query filter
|
||||||
*/
|
*/
|
||||||
@@ -166,11 +176,13 @@ export class SearchPageComponent implements OnInit, OnDestroy {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.dataProvider.search(searchOptions).then(
|
this.loading = !append;
|
||||||
async result => {
|
|
||||||
|
try {
|
||||||
|
const result = await this.dataProvider.search(searchOptions);
|
||||||
this.singleTypeResponse =
|
this.singleTypeResponse =
|
||||||
result.facets.find(facet => facet.field === 'type')?.buckets
|
result.facets.find(facet => facet.field === 'type')?.buckets.length ===
|
||||||
.length === 1;
|
1;
|
||||||
if (append) {
|
if (append) {
|
||||||
let items = await this.items;
|
let items = await this.items;
|
||||||
// append results
|
// append results
|
||||||
@@ -184,8 +196,7 @@ export class SearchPageComponent implements OnInit, OnDestroy {
|
|||||||
return result.data;
|
return result.data;
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
},
|
} catch (error) {
|
||||||
async error => {
|
|
||||||
const alert: HTMLIonAlertElement = await this.alertController.create({
|
const alert: HTMLIonAlertElement = await this.alertController.create({
|
||||||
buttons: ['Dismiss'],
|
buttons: ['Dismiss'],
|
||||||
header: 'Error',
|
header: 'Error',
|
||||||
@@ -193,8 +204,9 @@ export class SearchPageComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await alert.present();
|
await alert.present();
|
||||||
},
|
} finally {
|
||||||
);
|
this.loading = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -268,8 +280,14 @@ export class SearchPageComponent implements OnInit, OnDestroy {
|
|||||||
this.filterQuery = query[1];
|
this.filterQuery = query[1];
|
||||||
this.sortQuery = query[2];
|
this.sortQuery = query[2];
|
||||||
this.from = 0;
|
this.from = 0;
|
||||||
|
if (
|
||||||
|
typeof this.filterQuery !== 'undefined' ||
|
||||||
|
this.queryText?.length > 0 ||
|
||||||
|
this.showDefaultData
|
||||||
|
) {
|
||||||
await this.fetchAndUpdateItems();
|
await this.fetchAndUpdateItems();
|
||||||
this.queryChanged.next();
|
this.queryChanged.next();
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
this.settingsProvider.settingsActionChanged$.subscribe(
|
this.settingsProvider.settingsActionChanged$.subscribe(
|
||||||
({type, payload}) => {
|
({type, payload}) => {
|
||||||
|
|||||||
@@ -14,17 +14,26 @@
|
|||||||
(ngModelChange)="searchStringChanged($event)"
|
(ngModelChange)="searchStringChanged($event)"
|
||||||
[(ngModel)]="queryText"
|
[(ngModel)]="queryText"
|
||||||
showClearButton="always"
|
showClearButton="always"
|
||||||
|
placeholder="{{ 'search.search_bar.placeholder' | translate }}"
|
||||||
>
|
>
|
||||||
</ion-searchbar>
|
</ion-searchbar>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
|
|
||||||
<ion-content>
|
<ion-content>
|
||||||
|
<div
|
||||||
|
[style.display]="!showDefaultData && !items && !loading ? 'block' : 'none'"
|
||||||
|
>
|
||||||
|
<ion-label class="centeredMessageContainer">
|
||||||
|
{{ 'search.instruction' | translate }}
|
||||||
|
</ion-label>
|
||||||
|
</div>
|
||||||
<stapps-data-list
|
<stapps-data-list
|
||||||
id="data-list"
|
id="data-list"
|
||||||
[items]="items | async"
|
[items]="items | async"
|
||||||
[singleType]="singleTypeResponse"
|
[singleType]="singleTypeResponse"
|
||||||
(loadmore)="loadMore()"
|
(loadmore)="loadMore()"
|
||||||
[resetToTop]="queryChanged.asObservable()"
|
[resetToTop]="queryChanged.asObservable()"
|
||||||
|
[loading]="loading"
|
||||||
></stapps-data-list>
|
></stapps-data-list>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export class CatalogDetailContentComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
|
this.showDefaultData = true;
|
||||||
this.pageSize = 100;
|
this.pageSize = 100;
|
||||||
|
|
||||||
const nameSort: SCDucetSort = {
|
const nameSort: SCDucetSort = {
|
||||||
|
|||||||
@@ -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} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {FavoritesService} from './favorites.service';
|
import {FavoritesService} from './favorites.service';
|
||||||
import {DataRoutingService} from '../data/data-routing.service';
|
import {DataRoutingService} from '../data/data-routing.service';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
@@ -33,7 +33,10 @@ import {take} from 'rxjs/operators';
|
|||||||
providers: [ContextMenuService],
|
providers: [ContextMenuService],
|
||||||
styleUrls: ['./favorites-page.component.scss'],
|
styleUrls: ['./favorites-page.component.scss'],
|
||||||
})
|
})
|
||||||
export class FavoritesPageComponent extends SearchPageComponent {
|
export class FavoritesPageComponent
|
||||||
|
extends SearchPageComponent
|
||||||
|
implements OnInit
|
||||||
|
{
|
||||||
constructor(
|
constructor(
|
||||||
alertController: AlertController,
|
alertController: AlertController,
|
||||||
dataProvider: DataProvider,
|
dataProvider: DataProvider,
|
||||||
@@ -57,7 +60,8 @@ export class FavoritesPageComponent extends SearchPageComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ionViewWillEnter() {
|
ngOnInit() {
|
||||||
|
super.ngOnInit();
|
||||||
this.subscriptions.push(
|
this.subscriptions.push(
|
||||||
this.favoritesService.favoritesChanged$.subscribe(_favoritesMap => {
|
this.favoritesService.favoritesChanged$.subscribe(_favoritesMap => {
|
||||||
this.fetchAndUpdateItems();
|
this.fetchAndUpdateItems();
|
||||||
@@ -77,4 +81,8 @@ export class FavoritesPageComponent extends SearchPageComponent {
|
|||||||
this.updateContextFilter(result.facets);
|
this.updateContextFilter(result.facets);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
this.showDefaultData = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
(keyup)="searchKeyUp($event)"
|
(keyup)="searchKeyUp($event)"
|
||||||
[(ngModel)]="queryText"
|
[(ngModel)]="queryText"
|
||||||
(ionClear)="searchStringChanged()"
|
(ionClear)="searchStringChanged()"
|
||||||
placeholder="{{ 'map.page.search.PLACEHOLDER' | translate }}"
|
placeholder="{{ 'map.page.search_bar.placeholder' | translate }}"
|
||||||
showClearButton="always"
|
showClearButton="always"
|
||||||
>
|
>
|
||||||
</ion-searchbar>
|
</ion-searchbar>
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
</ion-col>
|
</ion-col>
|
||||||
</ion-row>
|
</ion-row>
|
||||||
</ion-grid>
|
</ion-grid>
|
||||||
<ion-label *ngIf="news.length === 0" class="notFoundContainer">
|
<ion-label *ngIf="news.length === 0" class="centeredMessageContainer">
|
||||||
{{ 'search.nothing_found' | translate | titlecase }}
|
{{ 'search.nothing_found' | translate | titlecase }}
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-infinite-scroll
|
<ion-infinite-scroll
|
||||||
|
|||||||
@@ -126,8 +126,8 @@
|
|||||||
"map": {
|
"map": {
|
||||||
"page": {
|
"page": {
|
||||||
"TITLE": "Karte",
|
"TITLE": "Karte",
|
||||||
"search": {
|
"search_bar": {
|
||||||
"PLACEHOLDER": "Gebäude, Points of Interest, Mensen und mehr"
|
"placeholder": "Gebäude, Points of Interest, Mensen und mehr"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"SHOW_LIST": "Liste ansehen",
|
"SHOW_LIST": "Liste ansehen",
|
||||||
@@ -177,6 +177,10 @@
|
|||||||
"title": "Aktuelles"
|
"title": "Aktuelles"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
|
"search_bar": {
|
||||||
|
"placeholder": "Suche ..."
|
||||||
|
},
|
||||||
|
"instruction": "Starte oben zu tippen, um Veranstaltungen, Veranstaltungstermine, Personen, Orte, Essen und mehr zu finden ...",
|
||||||
"nothing_found": "Keine Ergebnisse"
|
"nothing_found": "Keine Ergebnisse"
|
||||||
},
|
},
|
||||||
"schedule": {
|
"schedule": {
|
||||||
|
|||||||
@@ -126,8 +126,8 @@
|
|||||||
"map": {
|
"map": {
|
||||||
"page": {
|
"page": {
|
||||||
"TITLE": "Map",
|
"TITLE": "Map",
|
||||||
"search": {
|
"search_bar": {
|
||||||
"PLACEHOLDER": "Buildings, points of interests, canteens and more"
|
"placeholder": "Buildings, points of interests, canteens and more"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"SHOW_LIST": "Show list",
|
"SHOW_LIST": "Show list",
|
||||||
@@ -177,6 +177,10 @@
|
|||||||
"title": "News"
|
"title": "News"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
|
"search_bar": {
|
||||||
|
"placeholder": "Search ..."
|
||||||
|
},
|
||||||
|
"instruction": "Start typing above to find events, persons, places, food and more ...",
|
||||||
"nothing_found": "No results"
|
"nothing_found": "No results"
|
||||||
},
|
},
|
||||||
"schedule": {
|
"schedule": {
|
||||||
|
|||||||
@@ -41,13 +41,14 @@ ion-item, ion-card.compact {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.notFoundContainer {
|
.centeredMessageContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
min-height: 50vh;
|
min-height: 50vh;
|
||||||
|
margin: 20px;
|
||||||
|
|
||||||
ion-icon {
|
ion-icon {
|
||||||
font-size: 64px;
|
font-size: 64px;
|
||||||
|
|||||||
Reference in New Issue
Block a user