From a8f22c1e9679ca76b5bd0fd25fc83bc665a6e98a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jovan=20Kruni=C4=87?= Date: Thu, 15 Apr 2021 17:24:15 +0200 Subject: [PATCH] refactor: make data list reusable component Closes #98 --- src/app/modules/data/data-routing.module.ts | 8 +- .../modules/data/data-routing.service.spec.ts | 30 ++ src/app/modules/data/data-routing.service.ts | 46 +++ src/app/modules/data/data.module.ts | 8 +- src/app/modules/data/data.provider.ts | 6 +- .../data/list/data-list-item.component.ts | 10 + src/app/modules/data/list/data-list-item.html | 2 +- .../data/list/data-list.component.spec.ts | 25 ++ .../modules/data/list/data-list.component.ts | 229 +-------------- src/app/modules/data/list/data-list.html | 25 +- src/app/modules/data/list/data-list.scss | 3 - .../data/list/food-data-list.component.ts | 140 +++------ .../data/list/search-page.component.ts | 275 ++++++++++++++++++ src/app/modules/data/list/search-page.html | 19 ++ src/app/modules/data/list/search-page.scss | 0 15 files changed, 472 insertions(+), 354 deletions(-) create mode 100644 src/app/modules/data/data-routing.service.spec.ts create mode 100644 src/app/modules/data/data-routing.service.ts create mode 100644 src/app/modules/data/list/data-list.component.spec.ts create mode 100644 src/app/modules/data/list/search-page.component.ts create mode 100644 src/app/modules/data/list/search-page.html create mode 100644 src/app/modules/data/list/search-page.scss diff --git a/src/app/modules/data/data-routing.module.ts b/src/app/modules/data/data-routing.module.ts index ee06eac1..16787232 100644 --- a/src/app/modules/data/data-routing.module.ts +++ b/src/app/modules/data/data-routing.module.ts @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, 2019 StApps + * Copyright (C) 2018-2021 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. @@ -15,17 +15,17 @@ import {NgModule} from '@angular/core'; import {RouterModule, Routes} from '@angular/router'; import {DataDetailComponent} from './detail/data-detail.component'; -import {DataListComponent} from './list/data-list.component'; import {FoodDataListComponent} from './list/food-data-list.component'; +import {SearchPageComponent} from './list/search-page.component'; const dataRoutes: Routes = [ - {path: 'search', component: DataListComponent}, + {path: 'search', component: SearchPageComponent}, {path: 'data-detail/:uid', component: DataDetailComponent}, {path: 'canteen', component: FoodDataListComponent}, ]; /** - * TODO + * Module defining routes for data module */ @NgModule({ exports: [ diff --git a/src/app/modules/data/data-routing.service.spec.ts b/src/app/modules/data/data-routing.service.spec.ts new file mode 100644 index 00000000..c8a075e3 --- /dev/null +++ b/src/app/modules/data/data-routing.service.spec.ts @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2021 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 . + */ +import {TestBed} from '@angular/core/testing'; + +import {DataRoutingService} from './data-routing.service'; + +describe('DataRoutingService', () => { + let service: DataRoutingService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(DataRoutingService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/modules/data/data-routing.service.ts b/src/app/modules/data/data-routing.service.ts new file mode 100644 index 00000000..953b25be --- /dev/null +++ b/src/app/modules/data/data-routing.service.ts @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2021 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 . + */ +import {Injectable} from '@angular/core'; +import {SCThings} from '@openstapps/core'; +import {Subject} from 'rxjs'; + +/** + * Transmits event of data selection + */ +@Injectable({ + providedIn: 'root', +}) +export class DataRoutingService { + /** + * Provides the thing that was selected + */ + private childSelectedEvent = new Subject(); + + /** + * Provides the thing that was selected + * + * @param thing The selected thing + */ + emitChildEvent(thing: SCThings) { + this.childSelectedEvent.next(thing); + } + + /** + * Provides a listener for the event + */ + itemSelectListener() { + return this.childSelectedEvent.asObservable(); + } +} diff --git a/src/app/modules/data/data.module.ts b/src/app/modules/data/data.module.ts index 0bb59908..d33aac45 100644 --- a/src/app/modules/data/data.module.ts +++ b/src/app/modules/data/data.module.ts @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, 2019, 2020 StApps + * Copyright (C) 2018-2021 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. @@ -25,7 +25,7 @@ import {ThingTranslateModule} from '../../translation/thing-translate.module'; import {MenuModule} from '../menu/menu.module'; import {StorageModule} from '../storage/storage.module'; import {DataFacetsProvider} from './data-facets.provider'; -import { DataIconPipe } from './data-icon.pipe'; +import {DataIconPipe} from './data-icon.pipe'; import {DataRoutingModule} from './data-routing.module'; import {DataProvider} from './data.provider'; import {DataDetailContentComponent} from './detail/data-detail-content.component'; @@ -43,6 +43,7 @@ import {SkeletonSimpleCard} from './elements/skeleton-simple-card.component'; import {DataListItem} from './list/data-list-item.component'; import {DataListComponent} from './list/data-list.component'; import {FoodDataListComponent} from './list/food-data-list.component'; +import {SearchPageComponent} from './list/search-page.component'; import {StAppsWebHttpClient} from './stapps-web-http-client.provider'; import {ArticleDetailContentComponent} from './types/article/article-detail-content.component'; import {ArticleListItem} from './types/article/article-list-item.component'; @@ -71,7 +72,7 @@ import {VideoDetailContentComponent} from './types/video/video-detail-content.co import {VideoListItem} from './types/video/video-list-item.component'; /** - * TODO + * Module for handling data */ @NgModule({ declarations: [ @@ -109,6 +110,7 @@ import {VideoListItem} from './types/video/video-list-item.component'; PlaceDetailContentComponent, PlaceListItem, PlaceMensaDetailComponent, + SearchPageComponent, SemesterDetailContentComponent, SemesterListItem, SkeletonListItem, diff --git a/src/app/modules/data/data.provider.ts b/src/app/modules/data/data.provider.ts index fb0fbd53..e03ba6f0 100644 --- a/src/app/modules/data/data.provider.ts +++ b/src/app/modules/data/data.provider.ts @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, 2019 StApps + * Copyright (C) 2018-2021 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. @@ -31,7 +31,9 @@ export enum DataScope { * See https://angular.io/guide/dependency-injection for more info on providers * and Angular DI. */ -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class DataProvider { /** * TODO diff --git a/src/app/modules/data/list/data-list-item.component.ts b/src/app/modules/data/list/data-list-item.component.ts index d671016b..f05e3aa4 100644 --- a/src/app/modules/data/list/data-list-item.component.ts +++ b/src/app/modules/data/list/data-list-item.component.ts @@ -14,6 +14,7 @@ */ import {Component, Input} from '@angular/core'; import {SCThings} from '@openstapps/core'; +import {DataRoutingService} from '../data-routing.service'; /** * Shows data items in lists such es search result @@ -28,4 +29,13 @@ export class DataListItem { * An item to show */ @Input() item: SCThings; + + constructor(private dataRoutingService: DataRoutingService) {} + + /** + * Emit event that an item was selected + */ + notifySelect() { + this.dataRoutingService.emitChildEvent(this.item); + } } diff --git a/src/app/modules/data/list/data-list-item.html b/src/app/modules/data/list/data-list-item.html index 6020400c..768459b8 100644 --- a/src/app/modules/data/list/data-list-item.html +++ b/src/app/modules/data/list/data-list-item.html @@ -1,4 +1,4 @@ - + diff --git a/src/app/modules/data/list/data-list.component.spec.ts b/src/app/modules/data/list/data-list.component.spec.ts new file mode 100644 index 00000000..9cb68a4c --- /dev/null +++ b/src/app/modules/data/list/data-list.component.spec.ts @@ -0,0 +1,25 @@ +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; + +import {DataListComponent} from './data-list.component'; + +describe('DataListComponent', () => { + let component: DataListComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ DataListComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DataListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/modules/data/list/data-list.component.ts b/src/app/modules/data/list/data-list.component.ts index 4368571a..69a750a4 100644 --- a/src/app/modules/data/list/data-list.component.ts +++ b/src/app/modules/data/list/data-list.component.ts @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 StApps + * Copyright (C) 2018-2021 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. @@ -12,235 +12,26 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ -import {Component, OnInit} from '@angular/core'; -import {AlertController} from '@ionic/angular'; -import { - SCFacet, - SCSearchFilter, - SCSearchQuery, - SCSearchSort, - SCThing, -} from '@openstapps/core'; -import {NGXLogger} from 'ngx-logger'; -import {Subject, Subscription} from 'rxjs'; -import {debounceTime, distinctUntilChanged} from 'rxjs/operators'; -import {ContextMenuService} from '../../menu/context/context-menu.service'; -import {SettingsProvider} from '../../settings/settings.provider'; -import {DataProvider} from '../data.provider'; - +import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; +import {SCThings} from '@openstapps/core'; /** - * DataListComponent queries things and shows list of things and context menu + * Shows the list of items */ @Component({ selector: 'stapps-data-list', templateUrl: 'data-list.html', styleUrls: ['data-list.scss'], - providers: [ContextMenuService], }) export class DataListComponent implements OnInit { + // tslint:disable-next-line:completed-docs + @Input() items: SCThings[]; + // tslint:disable-next-line:completed-docs + @Output('loadmore') loadMore = new EventEmitter(); - /** - * Api query filter - */ - filterQuery: SCSearchFilter | undefined; - - /** - * Thing counter to start query the next page from - */ - from = 0; - - /** - * Container for queried things - */ - items: Promise; - - /** - * Page size of queries - */ - pageSize = 30; - - /** - * Search value from search bar - */ - queryText: string; - - /** - * Subject to handle search text changes - */ - queryTextChanged: Subject = new Subject(); - - /** - * Time to wait for search query if search text is changing - */ - searchQueryDueTime = 1000; - - /** - * Api query sorting - */ - sortQuery: SCSearchSort | undefined; - - /** - * Array of all subscriptions to Observables - */ - subscriptions: Subscription[] = []; - - /** - * - * @param alertController AlertController - * @param dataProvider DataProvider - * @param contextMenuService ContextMenuService - * @param settingsProvider SettingsProvider - * @param logger An angular logger - */ - constructor( - protected readonly alertController: AlertController, - protected dataProvider: DataProvider, - protected readonly contextMenuService: ContextMenuService, - protected readonly settingsProvider: SettingsProvider, - protected readonly logger: NGXLogger, - ) { - this.subscriptions.push(this.queryTextChanged - .pipe( - debounceTime(this.searchQueryDueTime), - distinctUntilChanged()) - .subscribe((model) => { - this.from = 0; - this.queryText = model; - this.fetchAndUpdateItems(); - })); - - this.subscriptions.push(this.contextMenuService.filterQueryChanged$.subscribe((query) => { - this.filterQuery = query; - this.from = 0; - this.fetchAndUpdateItems(); - })); - this.subscriptions.push(this.contextMenuService.sortQueryChanged$.subscribe((query) => { - this.sortQuery = query; - this.from = 0; - this.fetchAndUpdateItems(); - })); - - this.fetchAndUpdateItems(); - - /** - * Subscribe to 'settings.changed' events - */ - this.subscriptions.push(this.settingsProvider.settingsActionChanged$.subscribe(({type, payload}) => { - if (type === 'stapps.settings.changed') { - const {category, name, value} = payload!; - this.logger.log(`received event "settings.changed" with category: - ${category}, name: ${name}, value: ${JSON.stringify(value)}`); - } - }, - )); - } - - /** - * Fetches items with set query configuration - * - * @param append If true fetched data gets appended to existing, override otherwise (default false) - */ - protected async fetchAndUpdateItems(append = false): Promise { - // build query search options - const searchOptions: SCSearchQuery = { - from: this.from, - size: this.pageSize, - }; - - if (this.queryText && this.queryText.length > 0) { - // add query string - searchOptions.query = this.queryText; - } - - if (this.filterQuery) { - // add query filtering - searchOptions.filter = this.filterQuery; - } - - if (this.sortQuery) { - // add query sorting - searchOptions.sort = [this.sortQuery]; - } - - return this.dataProvider.search(searchOptions) - .then(async (result) => { - if (append) { - let items = await this.items; - // append results - items = items.concat(result.data); - this.items = (async () => items)(); - } else { - // override items with results - this.items = (async () => { - this.updateContextFilter(result.facets); - return result.data} )(); - } - }, async (err) => { - const alert: HTMLIonAlertElement = await this.alertController.create({ - buttons: ['Dismiss'], - header: 'Error', - subHeader: err.message, - }); - - await alert.present(); - }); - } - - /** - * Loads next page of things - */ - // tslint:disable-next-line:no-any - async loadMore(event: any): Promise { - this.from += this.pageSize; - await this.fetchAndUpdateItems(true); - event.target.complete(); - } - - /** - * Initialises the possible sort options in ContextMenuService - */ ngOnInit(): void { - this.contextMenuService.setContextSort({ - name: 'sort', - reversed: false, - value: 'relevance', - values: [ - { - reversible: false, - value: 'relevance', - }, - { - reversible: true, - value: 'name', - }, - { - reversible: true, - value: 'type', - }, - ], - }); } - /** - * Unsubscribe from Observables - */ - ngOnDestroy() { - for (let sub of this.subscriptions) { - sub.unsubscribe(); - } - } - - /** - * Search event of search bar - */ - searchStringChanged(queryValue: string) { - this.queryTextChanged.next(queryValue); - } - - /** - * Updates the possible filter options in ContextMenuService with facets - */ - updateContextFilter(facets: SCFacet[]) { - this.contextMenuService.updateContextFilter(facets); + notifyLoadMore(e: Event) { + this.loadMore.emit(e); } } diff --git a/src/app/modules/data/list/data-list.html b/src/app/modules/data/list/data-list.html index df699e8c..5d5ccac9 100644 --- a/src/app/modules/data/list/data-list.html +++ b/src/app/modules/data/list/data-list.html @@ -1,24 +1,5 @@ - - - - - - - - - - - - - - - - - - - - -
+ +
@@ -33,7 +14,7 @@ - + diff --git a/src/app/modules/data/list/data-list.scss b/src/app/modules/data/list/data-list.scss index cc9c32ca..e69de29b 100644 --- a/src/app/modules/data/list/data-list.scss +++ b/src/app/modules/data/list/data-list.scss @@ -1,3 +0,0 @@ -stapps-data-list { - -} diff --git a/src/app/modules/data/list/food-data-list.component.ts b/src/app/modules/data/list/food-data-list.component.ts index a2ce60e9..27cc3139 100644 --- a/src/app/modules/data/list/food-data-list.component.ts +++ b/src/app/modules/data/list/food-data-list.component.ts @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 StApps + * Copyright (C) 2018-2021 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. @@ -13,114 +13,54 @@ * this program. If not, see . */ import {Component} from '@angular/core'; -import { - SCSearchQuery, SCThing, -} from '@openstapps/core'; -import {DataListComponent} from './data-list.component'; +import {SearchPageComponent} from './search-page.component'; /** - * TODO + * Presents a list of places for eating/drinking */ @Component({ - selector: 'stapps-data-list', - templateUrl: 'data-list.html', + templateUrl: 'search-page.html', }) -export class FoodDataListComponent extends DataListComponent { +export class FoodDataListComponent extends SearchPageComponent { /** - * Fetches items with set query configuration - * - * @param append If true fetched data gets appended to existing, override otherwise (default false) + * Sets the forced filter to present only places for eating/drinking */ - protected async fetchAndUpdateItems(append = false): Promise { - try { - // build query search options - const searchOptions: SCSearchQuery = { - from: this.from, - size: this.pageSize, - }; - - if (this.queryText && this.queryText.length > 0) { - // add query string - searchOptions.query = this.queryText; - } - - if (this.sortQuery) { - // add query sorting - searchOptions.sort = [this.sortQuery]; - } - - searchOptions.filter = { - arguments: { - filters: [ - { - arguments: { - field: 'categories', - value: 'canteen', - }, - type: 'value', - }, - { - arguments: { - field: 'categories', - value: 'student canteen', - }, - type: 'value', - }, - { - arguments: { - field: 'categories', - value: 'cafe', - }, - type: 'value', - }, - { - arguments: { - field: 'categories', - value: 'restaurant', - }, - type: 'value', - }, - ], - operation: 'or', + initialize() { + this.forcedFilter = { + arguments: { + filters: [ + { + arguments: { + field: 'categories', + value: 'canteen', + }, + type: 'value', }, - type: 'boolean', - }; - - if (this.filterQuery !== undefined) { - searchOptions.filter = { + { arguments: { - filters: [ - searchOptions.filter, - this.filterQuery, - ], - operation: 'and', + field: 'categories', + value: 'student canteen', }, - type: 'boolean', - }; - } - - const result = await this.dataProvider.search(searchOptions); - - this.items = (async () => { - this.updateContextFilter(result.facets); - let items: SCThing[]; - if(append) { - items = (await this.items).concat(result.data); - } else { - items = result.data; - } - - return items; - })(); - } catch (err) { - const alert: HTMLIonAlertElement = await this.alertController.create({ - buttons: ['Dismiss'], - header: 'Error', - subHeader: err.message, - }); - - await alert.present(); - } + type: 'value', + }, + { + arguments: { + field: 'categories', + value: 'cafe', + }, + type: 'value', + }, + { + arguments: { + field: 'categories', + value: 'restaurant', + }, + type: 'value', + }, + ], + operation: 'or', + }, + type: 'boolean', + }; } - } diff --git a/src/app/modules/data/list/search-page.component.ts b/src/app/modules/data/list/search-page.component.ts new file mode 100644 index 00000000..add45892 --- /dev/null +++ b/src/app/modules/data/list/search-page.component.ts @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2018-2021 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 . + */ +import {Component, Input, OnInit} from '@angular/core'; +import {Router} from '@angular/router'; +import {AlertController} from '@ionic/angular'; +import { + SCFacet, + SCSearchFilter, + SCSearchQuery, + SCSearchSort, + SCThings, +} from '@openstapps/core'; +import {NGXLogger} from 'ngx-logger'; +import {Subject, Subscription} from 'rxjs'; +import {debounceTime, distinctUntilChanged} from 'rxjs/operators'; +import {ContextMenuService} from '../../menu/context/context-menu.service'; +import {SettingsProvider} from '../../settings/settings.provider'; +import {DataRoutingService} from '../data-routing.service'; +import {DataProvider} from '../data.provider'; + +/** + * SearchPageComponent queries things and shows list of things as search results and filter as context menu + */ +@Component({ + selector: 'stapps-search-page', + templateUrl: 'search-page.html', + providers: [ContextMenuService], +}) +export class SearchPageComponent implements OnInit { + /** + * Api query filter + */ + filterQuery: SCSearchFilter | undefined; + /** + * Filters the search should be initialized with + */ + @Input() forcedFilter?: SCSearchFilter; + /** + * Thing counter to start query the next page from + */ + from = 0; + /** + * Container for queried things + */ + items: Promise; + /** + * Page size of queries + */ + pageSize = 30; + /** + * Search value from search bar + */ + queryText: string; + /** + * Subject to handle search text changes + */ + queryTextChanged = new Subject(); + /** + * Time to wait for search query if search text is changing + */ + searchQueryDueTime = 1000; + /** + * Api query sorting + */ + sortQuery: SCSearchSort | undefined; + /** + * Array of all subscriptions to Observables + */ + subscriptions: Subscription[] = []; + + /** + * Injects the providers and creates subscriptions + * + * @param alertController AlertController + * @param dataProvider DataProvider + * @param contextMenuService ContextMenuService + * @param settingsProvider SettingsProvider + * @param logger An angular logger + * @param dataRoutingService DataRoutingService + * @param router Router + */ + constructor( + protected readonly alertController: AlertController, + protected dataProvider: DataProvider, + protected readonly contextMenuService: ContextMenuService, + protected readonly settingsProvider: SettingsProvider, + protected readonly logger: NGXLogger, + protected dataRoutingService: DataRoutingService, + protected router: Router, + ) { + this.initialize(); + + this.subscriptions.push(this.queryTextChanged + .pipe( + debounceTime(this.searchQueryDueTime), + distinctUntilChanged()) + .subscribe((model) => { + this.from = 0; + this.queryText = model; + this.fetchAndUpdateItems(); + })); + + this.subscriptions.push(this.contextMenuService.filterQueryChanged$.subscribe((query) => { + this.filterQuery = query; + this.from = 0; + this.fetchAndUpdateItems(); + })); + this.subscriptions.push(this.contextMenuService.sortQueryChanged$.subscribe((query) => { + this.sortQuery = query; + this.from = 0; + this.fetchAndUpdateItems(); + })); + + this.fetchAndUpdateItems(); + + /** + * Subscribe to 'settings.changed' events + */ + this.subscriptions.push(this.settingsProvider.settingsActionChanged$.subscribe(({type, payload}) => { + if (type === 'stapps.settings.changed') { + const {category, name, value} = payload!; + this.logger.log(`received event "settings.changed" with category: + ${category}, name: ${name}, value: ${JSON.stringify(value)}`); + } + }, + )); + + this.subscriptions.push(this.dataRoutingService.itemSelectListener() + .subscribe((item) => { + void this.router.navigate(['data-detail', item.uid]); + })); + } + + /** + * Fetches items with set query configuration + * + * @param append If true fetched data gets appended to existing, override otherwise (default false) + */ + protected async fetchAndUpdateItems(append = false): Promise { + // build query search options + const searchOptions: SCSearchQuery = { + from: this.from, + size: this.pageSize, + }; + const filters: SCSearchFilter[] = []; + + if (this.queryText && this.queryText.length > 0) { + // add query string + searchOptions.query = this.queryText; + } + + if (this.sortQuery) { + // add query sorting + searchOptions.sort = [this.sortQuery]; + } + + for (const filter of [this.forcedFilter, this.filterQuery]) { + if (typeof filter !== 'undefined') { + filters.push(filter); + } + } + if (filters.length > 0) { + searchOptions.filter = { + arguments: { + filters: filters, + operation: 'and', + }, + type: 'boolean', + }; + } + + return this.dataProvider.search(searchOptions) + .then(async (result) => { + if (append) { + let items = await this.items; + // append results + items = items.concat(result.data); + this.items = (async () => items)(); + } else { + // override items with results + this.items = (async () => { + this.updateContextFilter(result.facets); + + return result.data; + })(); + } + }, async (err) => { + const alert: HTMLIonAlertElement = await this.alertController.create({ + buttons: ['Dismiss'], + header: 'Error', + subHeader: err.message, + }); + + await alert.present(); + }); + } + + /** + * Set starting values (e.g. forced filter, which can be set in components inheriting this one) + */ + // tslint:disable-next-line:prefer-function-over-method + initialize() { + // nothing to do here + } + + /** + * Loads next page of things + */ + // tslint:disable-next-line:no-any + async loadMore(event: any): Promise { + this.from += this.pageSize; + await this.fetchAndUpdateItems(true); + event.target.complete(); + } + + /** + * Unsubscribe from Observables + */ + ngOnDestroy() { + for (const subscription of this.subscriptions) { + subscription.unsubscribe(); + } + } + + /** + * Initialises the possible sort options in ContextMenuService + */ + ngOnInit(): void { + this.contextMenuService.setContextSort({ + name: 'sort', + reversed: false, + value: 'relevance', + values: [ + { + reversible: false, + value: 'relevance', + }, + { + reversible: true, + value: 'name', + }, + { + reversible: true, + value: 'type', + }, + ], + }); + } + + /** + * Search event of search bar + */ + searchStringChanged(queryValue: string) { + this.queryTextChanged.next(queryValue); + } + + /** + * Updates the possible filter options in ContextMenuService with facets + */ + updateContextFilter(facets: SCFacet[]) { + this.contextMenuService.updateContextFilter(facets); + } +} diff --git a/src/app/modules/data/list/search-page.html b/src/app/modules/data/list/search-page.html new file mode 100644 index 00000000..0ccf08d6 --- /dev/null +++ b/src/app/modules/data/list/search-page.html @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/app/modules/data/list/search-page.scss b/src/app/modules/data/list/search-page.scss new file mode 100644 index 00000000..e69de29b