fix: update core and apply stricter tslint rules

This commit is contained in:
Michel Jonathan Schmitz
2019-07-10 12:38:29 +02:00
parent 03c317430a
commit 911492d064
67 changed files with 1291 additions and 507 deletions
@@ -1,3 +1,17 @@
/*
* 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/>.
*/
/* tslint:disable */ /* tslint:disable */
export const sampleResources = [{ export const sampleResources = [{
'errorNames': [], 'errorNames': [],
+50 -23
View File
@@ -20,10 +20,11 @@ import {SCAcademicEvent, SCArticle, SCBook, SCBuilding, SCCatalog,
import {Observable, of} from 'rxjs'; import {Observable, of} from 'rxjs';
import {sampleResources} from './resources/test-resources'; import {sampleResources} from './resources/test-resources';
// tslint:disable:no-magic-numbers
const sampleMessages: SCMessage[] = [ const sampleMessages: SCMessage[] = [
{ {
audiences: ['students'], audiences: ['students'],
message: 'Foo Message Text', messageBody: 'Foo Message Text',
name: 'Foo Message', name: 'Foo Message',
origin: { origin: {
indexed: 'SOME-DATE', indexed: 'SOME-DATE',
@@ -35,7 +36,7 @@ const sampleMessages: SCMessage[] = [
}, },
{ {
audiences: ['employees'], audiences: ['employees'],
message: 'Bar Message Text', messageBody: 'Bar Message Text',
name: 'Bar Message', name: 'Bar Message',
origin: { origin: {
indexed: 'SOME-DATE', indexed: 'SOME-DATE',
@@ -363,31 +364,46 @@ export const sampleThingsMap: {[key in SCThingType | string]: SCThing[]} = {
tour: [], tour: [],
video: [], video: [],
}; };
// tslint:enable:no-magic-numbers
// provides all or certain sample things to be used in tests or backendless development /**
* TODO
*
* provides all or certain sample things to be used in tests or backendless development
*/
export function getSampleThings(...uids: string[]): SCThing[] { export function getSampleThings(...uids: string[]): SCThing[] {
const sampleThings: SCThing[] = []; const sampleThings: SCThing[] = [];
if (typeof uids !== 'undefined' && uids.length > 0) { if (typeof uids !== 'undefined' && uids.length > 0) {
uids.forEach((uid) => { uids.forEach((uid) => {
Object.keys(sampleThingsMap).forEach((key) => { Object.keys(sampleThingsMap)
sampleThingsMap[key].forEach((thing) => { .forEach((key) => {
if (thing.uid === uid) { sampleThingsMap[key].forEach((thing) => {
sampleThings.push(thing); if (thing.uid === uid) {
} sampleThings.push(thing);
}
});
}); });
});
}); });
} else { } else {
Object.keys(sampleThingsMap).forEach((key) => { Object.keys(sampleThingsMap)
sampleThings.push(...sampleThingsMap[key]); .forEach((key) => {
}); sampleThings.push(...sampleThingsMap[key]);
});
} }
return sampleThings; return sampleThings;
} }
/**
* TODO
*/
@Injectable() @Injectable()
export class SampleThings { export class SampleThings {
/**
* TODO
*/
http: HttpClient; http: HttpClient;
constructor(http: HttpClient) { constructor(http: HttpClient) {
this.http = http; this.http = http;
} }
@@ -396,6 +412,28 @@ export class SampleThings {
// // return of(sampleDishes[0]); // // return of(sampleDishes[0]);
// } // }
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
getSampleThing(uid: string): Observable<any[]> {
// const jsonContent: any[] = await this.http.get('http://localhost:8100/assets/json/joined.json').toPromise();
const sampleThings: any[] = [];
for (const resource of sampleResources) {
if (resource.instance.uid as SCThingType === uid) {
sampleThings.push(resource.instance);
return of(sampleThings);
}
}
return of(sampleThings);
}
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
getSampleThings(): Observable<any[]> { getSampleThings(): Observable<any[]> {
// const jsonContent: any[] = await this.http.get('http://localhost:8100/assets/json/joined.json').toPromise(); // const jsonContent: any[] = await this.http.get('http://localhost:8100/assets/json/joined.json').toPromise();
const sampleThings: any[] = []; const sampleThings: any[] = [];
@@ -405,18 +443,7 @@ export class SampleThings {
sampleThings.push(resource.instance); sampleThings.push(resource.instance);
// } // }
}); });
return of(sampleThings);
}
getSampleThing(uid: string): Observable<any[]> {
// const jsonContent: any[] = await this.http.get('http://localhost:8100/assets/json/joined.json').toPromise();
const sampleThings: any[] = [];
for (const resource of sampleResources) {
if (resource.instance.uid as SCThingType === uid) {
sampleThings.push(resource.instance);
return of(sampleThings);
}
}
return of(sampleThings); return of(sampleThings);
} }
} }
+32 -21
View File
@@ -15,11 +15,12 @@
import {HTTP_INTERCEPTORS, HttpClient, import {HTTP_INTERCEPTORS, HttpClient,
HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from '@angular/common/http'; HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from '@angular/common/http';
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {SCIndexResponse, SCThingOriginType, SCThingType} from '@openstapps/core'; import {SCIndexResponse, SCSettingInputType, SCThingOriginType, SCThingType} from '@openstapps/core';
import {Observable, of} from 'rxjs'; import {Observable, of} from 'rxjs';
import {map, delay} from 'rxjs/operators'; import {delay, map} from 'rxjs/operators';
import {SampleThings} from './data/sample-things'; import {SampleThings} from './data/sample-things';
// tslint:disable:no-magic-numbers
const sampleIndexResponse: SCIndexResponse = { const sampleIndexResponse: SCIndexResponse = {
app: { app: {
campusPolygon: { campusPolygon: {
@@ -58,12 +59,9 @@ const sampleIndexResponse: SCIndexResponse = {
settings: [ settings: [
{ {
categories: ['profile'], categories: ['profile'],
defaultValue: 'student',
description: '', description: '',
input: { inputType: SCSettingInputType.SingleChoice,
defaultValue: 'student',
inputType: 'singleChoice',
values: ['student', 'employee', 'guest'],
},
name: 'group', name: 'group',
order: 1, order: 1,
origin: { origin: {
@@ -87,15 +85,13 @@ const sampleIndexResponse: SCIndexResponse = {
}, },
type: SCThingType.Setting, type: SCThingType.Setting,
uid: '', uid: '',
values: ['student', 'employee', 'guest'],
}, },
{ {
categories: ['profile'], categories: ['profile'],
defaultValue: 'en',
description: '', description: '',
input: { inputType: SCSettingInputType.SingleChoice,
defaultValue: 'en',
inputType: 'singleChoice',
values: ['en', 'de'],
},
name: 'language', name: 'language',
order: 0, order: 0,
origin: { origin: {
@@ -117,15 +113,13 @@ const sampleIndexResponse: SCIndexResponse = {
}, },
type: SCThingType.Setting, type: SCThingType.Setting,
uid: '', uid: '',
values: ['en', 'de'],
}, },
{ {
categories: ['privacy'], categories: ['privacy'],
defaultValue: false,
description: '', description: '',
input: { inputType: SCSettingInputType.SingleChoice,
defaultValue: false,
inputType: 'singleChoice',
values: [true, false],
},
name: 'geoLocation', name: 'geoLocation',
order: 0, order: 0,
origin: { origin: {
@@ -149,6 +143,7 @@ const sampleIndexResponse: SCIndexResponse = {
}, },
type: SCThingType.Setting, type: SCThingType.Setting,
uid: '', uid: '',
values: [true, false],
}, },
], ],
}, },
@@ -226,20 +221,33 @@ const sampleIndexResponse: SCIndexResponse = {
}, },
}; };
// tslint:enable:no-magic-numbers
/**
* TODO
*/
@Injectable() @Injectable()
export class FakeBackendInterceptor implements HttpInterceptor { export class FakeBackendInterceptor implements HttpInterceptor {
/**
* TODO
*/
sampleFetcher: SampleThings; sampleFetcher: SampleThings;
constructor(http: HttpClient) { constructor(http: HttpClient) {
this.sampleFetcher = new SampleThings(http); this.sampleFetcher = new SampleThings(http);
} }
/**
* TODO
*/
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (request.method === 'POST') { if (request.method === 'POST') {
if (request.url.endsWith('/') && request.method === 'POST') { if (request.url.endsWith('/') && request.method === 'POST') {
// respond with expected (faked) index response // respond with expected (faked) index response
return of(new HttpResponse({status: 200, body: sampleIndexResponse})); return of(new HttpResponse({status: 200, body: sampleIndexResponse}));
// respond with a search response with sample data // respond with a search response with sample data
} else if (request.url.endsWith('/search')) { }
if (request.url.endsWith('/search')) {
if (typeof request.body.filter !== 'undefined' && typeof request.body.filter.arguments !== 'undefined') { if (typeof request.body.filter !== 'undefined' && typeof request.body.filter.arguments !== 'undefined') {
if (request.body.filter.arguments.field === 'uid') { if (request.body.filter.arguments.field === 'uid') {
return this.sampleFetcher.getSampleThing(request.body.filter.arguments.value) return this.sampleFetcher.getSampleThing(request.body.filter.arguments.value)
@@ -248,11 +256,14 @@ export class FakeBackendInterceptor implements HttpInterceptor {
}), delay(1000)); // add delay for skeleton screens to be seen (see !16) }), delay(1000)); // add delay for skeleton screens to be seen (see !16)
} }
} }
return this.sampleFetcher.getSampleThings().pipe(map((sampleData: any) => {
return new HttpResponse({status: 200, body: {data: sampleData}}); return this.sampleFetcher.getSampleThings()
}), delay(1000)); // add delay for skeleton screens to be seen (see !16) .pipe(map((sampleData: any) => {
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);
} }
} }
+6 -3
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018 StApps * Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -16,11 +16,14 @@ import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router'; import {RouterModule, Routes} from '@angular/router';
const routes: Routes = [ const routes: Routes = [
{path: '', redirectTo: '/search', pathMatch: 'full'} {path: '', redirectTo: '/search', pathMatch: 'full'},
]; ];
/**
* TODO
*/
@NgModule({ @NgModule({
exports: [RouterModule], exports: [RouterModule],
imports: [RouterModule.forRoot(routes)] imports: [RouterModule.forRoot(routes)],
}) })
export class AppRoutingModule {} export class AppRoutingModule {}
+64 -39
View File
@@ -21,62 +21,87 @@ import {Logger} from '@openstapps/logger';
import {ConfigProvider} from './modules/config/config.provider'; import {ConfigProvider} from './modules/config/config.provider';
import {SettingsProvider} from './modules/settings/settings.provider'; import {SettingsProvider} from './modules/settings/settings.provider';
const logger: Logger = new Logger(); /**
* TODO
*/
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
templateUrl: 'app.component.html', templateUrl: 'app.component.html',
}) })
export class AppComponent { export class AppComponent {
logger: Logger = new Logger(); /**
pages: Array<{ component: any, title: string }>; * TODO
*/
pages: Array<{
/**
* TODO
*/
component: any;
/**
* TODO
*/
title: string;
}>;
constructor(private platform: Platform, /**
private statusBar: StatusBar, *
private splashScreen: SplashScreen, * @param platform TODO
private translateService: TranslateService, * @param statusBar TODO
private settingsProvider: SettingsProvider, * @param splashScreen TODO
private configProvider: ConfigProvider) { * @param translateService TODO
* @param settingsProvider TODO
* @param configProvider TODO
*/
constructor(private readonly platform: Platform,
private readonly statusBar: StatusBar,
private readonly splashScreen: SplashScreen,
private readonly translateService: TranslateService,
private readonly settingsProvider: SettingsProvider,
private readonly configProvider: ConfigProvider) {
this.initializeApp(); this.initializeApp();
// this language will be used as a fallback when a translation isn't found in the current language // this language will be used as a fallback when a translation isn't found in the current language
translateService.setDefaultLang('en'); translateService.setDefaultLang('en');
} }
/**
* TODO
*/
initializeApp() { initializeApp() {
this.platform.ready().then(async () => { this.platform.ready()
// Okay, so the platform is ready and our plugins are available. .then(async () => {
// Here you can do any higher level native things you might need. // Okay, so the platform is ready and our plugins are available.
this.statusBar.styleDefault(); // Here you can do any higher level native things you might need.
this.splashScreen.hide(); this.statusBar.styleDefault();
this.splashScreen.hide();
// initialise the configProvider // initialise the configProvider
try { try {
await this.configProvider.init(); await this.configProvider.init();
} catch (error) { } catch (error) {
if (typeof error.name !== 'undefined') { if (typeof error.name !== 'undefined') {
if (error.name === 'ConfigInitError') { if (error.name === 'ConfigInitError') {
// @TODO: Issue #43 handle initialisation error and inform user // @TODO: Issue #43 handle initialisation error and inform user
}
} }
await Logger.error(error);
} }
logger.error(error);
}
// set order of categories in settings // set order of categories in settings
this.settingsProvider.setCategoriesOrder([ this.settingsProvider.setCategoriesOrder([
'profile', 'profile',
'privacy', 'privacy',
'credentials', 'credentials',
'others', 'others',
]); ]);
try { try {
// set language from settings // set language from settings
const languageCode = await this.settingsProvider.getValue('profile', 'language'); const languageCode = await this.settingsProvider.getValue('profile', 'language');
this.translateService.use(languageCode); this.translateService.use(languageCode);
} catch (error) { } catch (error) {
this.logger.warn(error); Logger.warn(error);
} }
}); });
} }
} }
+11 -3
View File
@@ -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 {CommonModule, registerLocaleData, LocationStrategy, HashLocationStrategy} from '@angular/common'; import {CommonModule, HashLocationStrategy, LocationStrategy, registerLocaleData} from '@angular/common';
import {HttpClient} from '@angular/common/http'; import {HttpClient} from '@angular/common/http';
import localeDe from '@angular/common/locales/de'; import localeDe from '@angular/common/locales/de';
import {NgModule} from '@angular/core'; import {NgModule} from '@angular/core';
@@ -34,10 +34,18 @@ import {StorageModule} from './modules/storage/storage.module';
registerLocaleData(localeDe); registerLocaleData(localeDe);
/**
* TODO
*
* @param http TODO
*/
export function createTranslateLoader(http: HttpClient) { export function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json'); return new TranslateHttpLoader(http, './assets/i18n/', '.json');
} }
/**
* TODO
*/
@NgModule({ @NgModule({
bootstrap: [AppComponent], bootstrap: [AppComponent],
declarations: [AppComponent], declarations: [AppComponent],
@@ -66,11 +74,11 @@ export function createTranslateLoader(http: HttpClient) {
SplashScreen, SplashScreen,
{ {
provide: RouteReuseStrategy, provide: RouteReuseStrategy,
useClass: IonicRouteStrategy useClass: IonicRouteStrategy,
}, },
{ {
provide: LocationStrategy, provide: LocationStrategy,
useClass: HashLocationStrategy useClass: HashLocationStrategy,
}, },
], ],
}) })
+3
View File
@@ -17,6 +17,9 @@ import {DataModule} from '../data/data.module';
import {StorageModule} from '../storage/storage.module'; import {StorageModule} from '../storage/storage.module';
import {ConfigProvider} from './config.provider'; import {ConfigProvider} from './config.provider';
/**
* TODO
*/
@NgModule({ @NgModule({
imports: [ imports: [
StorageModule, StorageModule,
@@ -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 {TestBed} from '@angular/core/testing'; import {TestBed} from '@angular/core/testing';
import {SCIndexResponse, SCThingOriginType, SCThingType} from '@openstapps/core'; import {SCIndexResponse, SCThingOriginType, SCThingType, SCSettingInputType} from '@openstapps/core';
import {StAppsWebHttpClient} from '../data/stapps-web-http-client.provider'; import {StAppsWebHttpClient} from '../data/stapps-web-http-client.provider';
import {StorageProvider} from '../storage/storage.provider'; import {StorageProvider} from '../storage/storage.provider';
import {ConfigProvider, STORAGE_KEY_CONFIG} from './config.provider'; import {ConfigProvider, STORAGE_KEY_CONFIG} from './config.provider';
@@ -207,10 +207,8 @@ const sampleIndexResponse: SCIndexResponse = {
settings: [ settings: [
{ {
categories: ['credentials'], categories: ['credentials'],
input: { defaultValue: '',
defaultValue: '', inputType: SCSettingInputType.Text,
inputType: 'text',
},
name: 'username', name: 'username',
order: 0, order: 0,
origin: { origin: {
+25 -11
View File
@@ -39,12 +39,25 @@ export const STORAGE_KEY_CONFIG = 'stapps.config';
*/ */
@Injectable() @Injectable()
export class ConfigProvider { export class ConfigProvider {
/**
* TODO
*/
client: Client; client: Client;
/**
* TODO
*/
config: SCIndexResponse; config: SCIndexResponse;
initialised: boolean = false; /**
logger: Logger = new Logger(); * TODO
*/
initialised = false;
constructor(private storageProvider: StorageProvider, swHttpClient: StAppsWebHttpClient) { /**
*
* @param storageProvider TODO
* @param swHttpClient TODO
*/
constructor(private readonly storageProvider: StorageProvider, swHttpClient: StAppsWebHttpClient) {
this.client = new Client(swHttpClient, environment.backend_url, environment.backend_version); this.client = new Client(swHttpClient, environment.backend_url, environment.backend_version);
} }
@@ -77,9 +90,8 @@ export class ConfigProvider {
} }
if (typeof this.config.app[attribute] !== 'undefined') { if (typeof this.config.app[attribute] !== 'undefined') {
return this.config.app[attribute]; return this.config.app[attribute];
} else {
throw new ConfigValueNotAvailable(attribute);
} }
throw new ConfigValueNotAvailable(attribute);
} }
/** /**
@@ -96,10 +108,10 @@ export class ConfigProvider {
try { try {
this.config = await this.loadLocal(); this.config = await this.loadLocal();
this.initialised = true; this.initialised = true;
this.logger.log(`initialised configuration from storage: ${JSON.stringify(this.config)}`); Logger.log(`initialised configuration from storage: ${JSON.stringify(this.config)}`);
if (this.config.backend.SCVersion !== environment.backend_version) { if (this.config.backend.SCVersion !== environment.backend_version) {
loadError = new WrongConfigVersionInStorage(environment.backend_version, this.config.backend.SCVersion); loadError = new WrongConfigVersionInStorage(environment.backend_version, this.config.backend.SCVersion);
this.logger.warn(loadError); Logger.warn(loadError);
} }
} catch (error) { } catch (error) {
loadError = error; loadError = error;
@@ -109,16 +121,18 @@ export class ConfigProvider {
const fetchedConfig: SCIndexResponse = await this.fetch(); const fetchedConfig: SCIndexResponse = await this.fetch();
await this.set(fetchedConfig); await this.set(fetchedConfig);
this.initialised = true; this.initialised = true;
this.logger.log(`initialised configuration from remote: ${JSON.stringify(this.config)}`); Logger.log(`initialised configuration from remote: ${JSON.stringify(this.config)}`);
} catch (error) { } catch (error) {
fetchError = error; fetchError = error;
} }
// check for occurred errors and throw them // check for occurred errors and throw them
if (typeof loadError !== 'undefined' && typeof fetchError !== 'undefined') { if (typeof loadError !== 'undefined' && typeof fetchError !== 'undefined') {
throw new ConfigInitError(); throw new ConfigInitError();
} else if (typeof loadError !== 'undefined') { }
if (typeof loadError !== 'undefined') {
throw loadError; throw loadError;
} else if (typeof fetchError !== 'undefined') { }
if (typeof fetchError !== 'undefined') {
throw fetchError; throw fetchError;
} }
} }
@@ -132,7 +146,7 @@ export class ConfigProvider {
await this.storageProvider.init(); await this.storageProvider.init();
// get local configuration // get local configuration
if (await this.storageProvider.has(STORAGE_KEY_CONFIG)) { if (await this.storageProvider.has(STORAGE_KEY_CONFIG)) {
return await this.storageProvider.get<SCIndexResponse>(STORAGE_KEY_CONFIG); return this.storageProvider.get<SCIndexResponse>(STORAGE_KEY_CONFIG);
} }
throw new SavedConfigNotAvailable(); throw new SavedConfigNotAvailable();
} }
+66 -54
View File
@@ -13,8 +13,11 @@
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {SCBackendAggregationConfiguration, SCFacet, SCThing, SCFacetBucket} from '@openstapps/core'; import {SCBackendAggregationConfiguration, SCFacet, SCFacetBucket, SCThing} from '@openstapps/core';
/**
* TODO
*/
@Injectable() @Injectable()
export class DataFacetsProvider { export class DataFacetsProvider {
// tslint:disable-next-line:no-empty // tslint:disable-next-line:no-empty
@@ -29,7 +32,8 @@ export class DataFacetsProvider {
* @param bucketsMap Buckets array transformed into a map * @param bucketsMap Buckets array transformed into a map
* @param fields A field that should be added to buckets (its map) * @param fields A field that should be added to buckets (its map)
*/ */
addBuckets(bucketsMap: {[key: string]: number}, fields: string[]): {[key: string]: number} { // tslint:disable-next-line:prefer-function-over-method
addBuckets(bucketsMap: {[key: string]: number; }, fields: string[]): {[key: string]: number; } {
fields.forEach((field) => { fields.forEach((field) => {
if (typeof bucketsMap[field] !== 'undefined') { if (typeof bucketsMap[field] !== 'undefined') {
bucketsMap[field] = bucketsMap[field] + 1; bucketsMap[field] = bucketsMap[field] + 1;
@@ -37,6 +41,7 @@ export class DataFacetsProvider {
bucketsMap[field] = 1; bucketsMap[field] = 1;
} }
}); });
return bucketsMap; return bucketsMap;
} }
@@ -45,61 +50,16 @@ export class DataFacetsProvider {
* *
* @param buckets Buckets from a facet * @param buckets Buckets from a facet
*/ */
bucketsToMap(buckets: SCFacetBucket[]): {[key: string]: number} { // tslint:disable-next-line:prefer-function-over-method
const bucketsMap: {[key: string]: number} = {}; bucketsToMap(buckets: SCFacetBucket[]): {[key: string]: number; } {
const bucketsMap: {[key: string]: number; } = {};
buckets.forEach((bucket) => { buckets.forEach((bucket) => {
bucketsMap[bucket.key] = bucket.count; bucketsMap[bucket.key] = bucket.count;
}); });
return bucketsMap; return bucketsMap;
} }
/**
* Converts a buckets map into buckets array (as it is inside of a facet)
*
* @param bucketsMap A map from a buckets array
*/
mapToBuckets(bucketsMap: {[key: string]: number}): SCFacetBucket[] {
const buckets: SCFacetBucket[] = [];
for (const key in bucketsMap) {
if (bucketsMap.hasOwnProperty(key)) {
const bucket: SCFacetBucket = {key: key, count: bucketsMap[key]};
buckets.push(bucket);
}
}
return buckets;
}
/**
* Converts facets array into a map (for quicker operations with facets)
*
* @param facets Array of facets
*/
facetsToMap(facets: SCFacet[]): {[key: string]: {[key: string]: number}} {
const facetsMap: {[key: string]: {[key: string]: number}} = {};
facets.forEach((facet) => {
facetsMap[facet.field] = this.bucketsToMap(facet.buckets);
});
return facetsMap;
}
/**
* Converts facets map into an array of facets (as they are provided by backend)
*
* @param facetsMap A map from facets array
*/
mapToFacets(facetsMap: {[key: string]: {[key: string]: number}}): SCFacet[] {
const facets: SCFacet[] = [];
for (const key in facetsMap) {
if (facetsMap.hasOwnProperty(key)) {
const facet: SCFacet = {buckets: [], field: ''};
facet.field = key;
facet.buckets = this.mapToBuckets(facetsMap[key]);
facets.push(facet);
}
}
return facets;
}
/** /**
* Extract facets from data items, optionally combine them with a list of existing facets * Extract facets from data items, optionally combine them with a list of existing facets
* *
@@ -114,12 +74,12 @@ export class DataFacetsProvider {
if (items.length === 0) { if (items.length === 0) {
if (facets.length === 0) { if (facets.length === 0) {
return []; return [];
} else {
return facets;
} }
return facets;
} }
const combinedFacets: SCFacet[] = facets; const combinedFacets: SCFacet[] = facets;
const combinedFacetsMap: {[key: string]: {[key: string]: number}} = this.facetsToMap(combinedFacets); const combinedFacetsMap: {[key: string]: {[key: string]: number; }; } = this.facetsToMap(combinedFacets);
(items as any[]).forEach((item) => { (items as any[]).forEach((item) => {
aggregations.forEach((aggregation) => { aggregations.forEach((aggregation) => {
let fieldValues: string | string[] = item[aggregation.fieldName]; let fieldValues: string | string[] = item[aggregation.fieldName];
@@ -142,6 +102,58 @@ export class DataFacetsProvider {
} }
}); });
}); });
return this.mapToFacets(combinedFacetsMap); return this.mapToFacets(combinedFacetsMap);
} }
/**
* Converts facets array into a map (for quicker operations with facets)
*
* @param facets Array of facets
*/
facetsToMap(facets: SCFacet[]): {[key: string]: {[key: string]: number; }; } {
const facetsMap: {[key: string]: {[key: string]: number; }; } = {};
facets.forEach((facet) => {
facetsMap[facet.field] = this.bucketsToMap(facet.buckets);
});
return facetsMap;
}
/**
* Converts a buckets map into buckets array (as it is inside of a facet)
*
* @param bucketsMap A map from a buckets array
*/
// tslint:disable-next-line:prefer-function-over-method
mapToBuckets(bucketsMap: {[key: string]: number; }): SCFacetBucket[] {
const buckets: SCFacetBucket[] = [];
for (const key in bucketsMap) {
if (bucketsMap.hasOwnProperty(key)) {
const bucket: SCFacetBucket = {key: key, count: bucketsMap[key]};
buckets.push(bucket);
}
}
return buckets;
}
/**
* Converts facets map into an array of facets (as they are provided by backend)
*
* @param facetsMap A map from facets array
*/
mapToFacets(facetsMap: {[key: string]: {[key: string]: number; }; }): SCFacet[] {
const facets: SCFacet[] = [];
for (const key in facetsMap) {
if (facetsMap.hasOwnProperty(key)) {
const facet: SCFacet = {buckets: [], field: ''};
facet.field = key;
facet.buckets = this.mapToBuckets(facetsMap[key]);
facets.push(facet);
}
}
return facets;
}
} }
@@ -22,6 +22,9 @@ const dataRoutes: Routes = [
{path: 'data-detail/:uid', component: DataDetailComponent}, {path: 'data-detail/:uid', component: DataDetailComponent},
]; ];
/**
* TODO
*/
@NgModule({ @NgModule({
exports: [ exports: [
RouterModule, RouterModule,
+7 -4
View File
@@ -26,13 +26,15 @@ import {DataRoutingModule} from './data-routing.module';
import {DataProvider} from './data.provider'; import {DataProvider} from './data.provider';
import {DataDetailContentComponent} from './detail/data-detail-content.component'; import {DataDetailContentComponent} from './detail/data-detail-content.component';
import {DataDetailComponent} from './detail/data-detail.component'; import {DataDetailComponent} from './detail/data-detail.component';
import {OffersDetailComponent} from './elements/offers-detail.component';
import {OffersInListComponent} from './elements/offers-in-list.component';
import {AddressDetailComponent} from './elements/address-detail.component'; import {AddressDetailComponent} from './elements/address-detail.component';
import {LongInlineText} from './elements/long-inline-text.component'; import {LongInlineText} from './elements/long-inline-text.component';
import {OffersDetailComponent} from './elements/offers-detail.component';
import {OffersInListComponent} from './elements/offers-in-list.component';
import {OriginDetailComponent} from './elements/origin-detail.component'; import {OriginDetailComponent} from './elements/origin-detail.component';
import {OriginInListComponent} from './elements/origin-in-list.component'; import {OriginInListComponent} from './elements/origin-in-list.component';
import {SimpleCardComponent} from './elements/simple-card.component'; import {SimpleCardComponent} from './elements/simple-card.component';
import {SkeletonListItem} from './elements/skeleton-list-item.component';
import {SkeletonSimpleCard} from './elements/skeleton-simple-card.component';
import {DataListItem} from './list/data-list-item.component'; import {DataListItem} from './list/data-list-item.component';
import {DataListComponent} from './list/data-list.component'; import {DataListComponent} from './list/data-list.component';
import {StAppsWebHttpClient} from './stapps-web-http-client.provider'; import {StAppsWebHttpClient} from './stapps-web-http-client.provider';
@@ -60,9 +62,10 @@ 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';
/**
* TODO
*/
@NgModule({ @NgModule({
declarations: [ declarations: [
OffersDetailComponent, OffersDetailComponent,
+76 -45
View File
@@ -24,41 +24,64 @@ export enum DataScope {
Remote = 'remote', Remote = 'remote',
} }
/* /**
Generated class for the DataProvider provider. * Generated class for the DataProvider provider.
*
See https://angular.io/guide/dependency-injection for more info on providers * See https://angular.io/guide/dependency-injection for more info on providers
and Angular DI. * and Angular DI.
*/ */
@Injectable() @Injectable()
export class DataProvider { export class DataProvider {
private _storagePrefix: string = 'stapps.data'; /**
// @TODO: get backendUrl and appVersion and storagePrefix from config (module) * TODO
appVersion: string = '1.0.6'; */
backendUrl: string = 'http://localhost:3000'; private _storagePrefix = 'stapps.data';
/**
* TODO
*
* @TODO: get backendUrl and appVersion and storagePrefix from config (module)
*/
appVersion = '1.0.6';
/**
* TODO
*/
backendUrl = 'http://localhost:3000';
/**
* TODO
*/
client: Client; client: Client;
/**
* TODO
*/
storageProvider: StorageProvider; storageProvider: StorageProvider;
/**
* TODO
*
* @param stAppsWebHttpClient TODO
* @param storageProvider TODO
*/
constructor(stAppsWebHttpClient: StAppsWebHttpClient, storageProvider: StorageProvider) { constructor(stAppsWebHttpClient: StAppsWebHttpClient, storageProvider: StorageProvider) {
this.client = new Client(stAppsWebHttpClient, this.backendUrl, this.appVersion); this.client = new Client(stAppsWebHttpClient, this.backendUrl, this.appVersion);
this.storageProvider = storageProvider; this.storageProvider = storageProvider;
} }
get storagePrefix(): string { /**
return this._storagePrefix; * Delete a data item
} *
* @param uid Unique identifier of the saved data item
set storagePrefix(storagePrefix) { */
this._storagePrefix = storagePrefix; async delete(uid: string): Promise<void> {
return this.storageProvider.delete(this.getDataKey(uid));
} }
/** /**
* Provides key for storing data into the local database * Delete all the previously saved data items
*
* @param uid Unique identifier of a resource
*/ */
getDataKey(uid: string): string { async deleteAll(): Promise<void> {
return `${this.storagePrefix}.${uid}`; const keys = Array.from((await this.getAll()).keys());
return this.storageProvider.delete(...keys);
} }
/** /**
@@ -91,6 +114,7 @@ export class DataProvider {
const map: Map<DataScope, SCThings | SCSaveableThing<SCThings>> = new Map(); const map: Map<DataScope, SCThings | SCSaveableThing<SCThings>> = new Map();
map.set(DataScope.Local, await this.get(uid, DataScope.Local)); map.set(DataScope.Local, await this.get(uid, DataScope.Local));
map.set(DataScope.Remote, await this.get(uid, DataScope.Remote)); map.set(DataScope.Remote, await this.get(uid, DataScope.Remote));
return map; return map;
} }
@@ -101,6 +125,24 @@ export class DataProvider {
return this.storageProvider.search<SCSaveableThing<SCThings>>(this.storagePrefix); return this.storageProvider.search<SCSaveableThing<SCThings>>(this.storagePrefix);
} }
/**
* Provides key for storing data into the local database
*
* @param uid Unique identifier of a resource
*/
getDataKey(uid: string): string {
return `${this.storagePrefix}.${uid}`;
}
/**
* Provides information if something with an UID is saved as a data item
*
* @param uid Unique identifier of the saved data item
*/
async isSaved(uid: string): Promise<boolean> {
return this.storageProvider.has(this.getDataKey(uid));
}
/** /**
* Save a data item * Save a data item
* *
@@ -118,25 +160,9 @@ export class DataProvider {
type: (typeof type === 'undefined') ? item.type : type, type: (typeof type === 'undefined') ? item.type : type,
uid: item.uid, uid: item.uid,
}; };
// @TODO: Implementation for saving item into the backend (user's account) // @TODO: Implementation for saving item into the backend (user's account)
return (await this.storageProvider.put<SCSaveableThing<SCThings>>(this.getDataKey(item.uid), saveableItem)); return ( this.storageProvider.put<SCSaveableThing<SCThings>>(this.getDataKey(item.uid), saveableItem));
}
/**
* Delete a data item
*
* @param uid Unique identifier of the saved data item
*/
async delete(uid: string): Promise<void> {
return this.storageProvider.delete(this.getDataKey(uid));
}
/**
* Delete all the previously saved data items
*/
async deleteAll(): Promise<void> {
const keys = Array.from((await this.getAll()).keys());
return this.storageProvider.delete(...keys);
} }
/** /**
@@ -145,15 +171,20 @@ export class DataProvider {
* @param query - query to send to the backend * @param query - query to send to the backend
*/ */
async search(query: SCSearchQuery): Promise<SCSearchResponse> { async search(query: SCSearchQuery): Promise<SCSearchResponse> {
return (await this.client.search(query)); return (this.client.search(query));
} }
/** /**
* Provides information if something with an UID is saved as a data item * TODO
*
* @param uid Unique identifier of the saved data item
*/ */
async isSaved(uid: string): Promise<boolean> { get storagePrefix(): string {
return this.storageProvider.has(this.getDataKey(uid)); return this._storagePrefix;
}
/**
* TODO
*/
set storagePrefix(storagePrefix) {
this._storagePrefix = storagePrefix;
} }
} }
@@ -15,10 +15,16 @@
import {Component, Input} from '@angular/core'; import {Component, Input} from '@angular/core';
import {SCThings} from '@openstapps/core'; import {SCThings} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-data-detail-content', selector: 'stapps-data-detail-content',
templateUrl: 'data-detail-content.html', templateUrl: 'data-detail-content.html',
}) })
export class DataDetailContentComponent { export class DataDetailContentComponent {
/**
* TODO
*/
@Input() item: SCThings; @Input() item: SCThings;
} }
@@ -19,17 +19,35 @@ import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
import {SCLanguageCode, SCThing, SCUuid} from '@openstapps/core'; import {SCLanguageCode, SCThing, SCUuid} from '@openstapps/core';
import {DataProvider, DataScope} from '../data.provider'; import {DataProvider, DataScope} from '../data.provider';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-data-detail', selector: 'stapps-data-detail',
styleUrls: ['data-detail.scss'], styleUrls: ['data-detail.scss'],
templateUrl: 'data-detail.html', templateUrl: 'data-detail.html',
}) })
export class DataDetailComponent { export class DataDetailComponent {
/**
* TODO
*/
dataProvider: DataProvider; dataProvider: DataProvider;
/**
* TODO
*/
item: SCThing; item: SCThing;
/**
* TODO
*/
language: SCLanguageCode; language: SCLanguageCode;
constructor(private route: ActivatedRoute, dataProvider: DataProvider, translateService: TranslateService) { /**
*
* @param route TODO
* @param dataProvider TODO
* @param translateService TODO
*/
constructor(private readonly route: ActivatedRoute, dataProvider: DataProvider, translateService: TranslateService) {
this.dataProvider = dataProvider; this.dataProvider = dataProvider;
this.language = translateService.currentLang as SCLanguageCode; this.language = translateService.currentLang as SCLanguageCode;
translateService.onLangChange.subscribe((event: LangChangeEvent) => { translateService.onLangChange.subscribe((event: LangChangeEvent) => {
@@ -43,11 +61,16 @@ export class DataDetailComponent {
* @param uid Unique identifier of a thing * @param uid Unique identifier of a thing
*/ */
async getItem(uid: SCUuid): Promise<void> { async getItem(uid: SCUuid): Promise<void> {
this.dataProvider.get(uid, DataScope.Remote).then((data) => { await this.dataProvider.get(uid, DataScope.Remote)
.then((data) => {
this.item = data; this.item = data;
}); });
} }
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() { ngOnInit() {
this.getItem(this.route.snapshot.paramMap.get('uid') || ''); this.getItem(this.route.snapshot.paramMap.get('uid') || '');
} }
@@ -15,10 +15,16 @@
import {Component, Input} from '@angular/core'; import {Component, Input} from '@angular/core';
import {SCPostalAddress} from '@openstapps/core'; import {SCPostalAddress} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-address-detail', selector: 'stapps-address-detail',
templateUrl: 'address-detail.html', templateUrl: 'address-detail.html',
}) })
export class AddressDetailComponent { export class AddressDetailComponent {
/**
* TODO
*/
@Input() address: SCPostalAddress; @Input() address: SCPostalAddress;
} }
@@ -14,11 +14,20 @@
*/ */
import {Component, Input} from '@angular/core'; import {Component, Input} from '@angular/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-long-inline-text', selector: 'stapps-long-inline-text',
templateUrl: 'long-inline-text.html', templateUrl: 'long-inline-text.html',
}) })
export class LongInlineText { export class LongInlineText {
@Input() text: string; /**
* TODO
*/
@Input() size: number; @Input() size: number;
/**
* TODO
*/
@Input() text: string;
} }
@@ -15,11 +15,20 @@
import {Component, Input} from '@angular/core'; import {Component, Input} from '@angular/core';
import {SCAcademicPriceGroup, SCThingThatCanBeOfferedOffer} from '@openstapps/core'; import {SCAcademicPriceGroup, SCThingThatCanBeOfferedOffer} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-offers-detail', selector: 'stapps-offers-detail',
templateUrl: 'offers-detail.html', templateUrl: 'offers-detail.html',
}) })
export class OffersDetailComponent { export class OffersDetailComponent {
/**
* TODO
*/
objectKeys = Object.keys; objectKeys = Object.keys;
/**
* TODO
*/
@Input() offers: Array<SCThingThatCanBeOfferedOffer<SCAcademicPriceGroup>>; @Input() offers: Array<SCThingThatCanBeOfferedOffer<SCAcademicPriceGroup>>;
} }
@@ -15,10 +15,16 @@
import {Component, Input} from '@angular/core'; import {Component, Input} from '@angular/core';
import {SCAcademicPriceGroup, SCThingThatCanBeOfferedOffer} from '@openstapps/core'; import {SCAcademicPriceGroup, SCThingThatCanBeOfferedOffer} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-offers-in-list', selector: 'stapps-offers-in-list',
templateUrl: 'offers-in-list.html', templateUrl: 'offers-in-list.html',
}) })
export class OffersInListComponent { export class OffersInListComponent {
/**
* TODO
*/
@Input() offers: Array<SCThingThatCanBeOfferedOffer<SCAcademicPriceGroup>>; @Input() offers: Array<SCThingThatCanBeOfferedOffer<SCAcademicPriceGroup>>;
} }
@@ -15,10 +15,16 @@
import {Component, Input} from '@angular/core'; import {Component, Input} from '@angular/core';
import {SCThingOrigin} from '@openstapps/core'; import {SCThingOrigin} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-origin-detail', selector: 'stapps-origin-detail',
templateUrl: 'origin-detail.html', templateUrl: 'origin-detail.html',
}) })
export class OriginDetailComponent { export class OriginDetailComponent {
/**
* TODO
*/
@Input() origin: SCThingOrigin; @Input() origin: SCThingOrigin;
} }
@@ -15,10 +15,16 @@
import {Component, Input} from '@angular/core'; import {Component, Input} from '@angular/core';
import {SCThingOrigin} from '@openstapps/core'; import {SCThingOrigin} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-origin-in-list', selector: 'stapps-origin-in-list',
templateUrl: 'origin-in-list.html', templateUrl: 'origin-in-list.html',
}) })
export class OriginInListComponent { export class OriginInListComponent {
/**
* TODO
*/
@Input() origin: SCThingOrigin; @Input() origin: SCThingOrigin;
} }
@@ -15,18 +15,41 @@
import {Component, Input} from '@angular/core'; import {Component, Input} from '@angular/core';
import {isThing, SCThing} from '@openstapps/core'; import {isThing, SCThing} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-simple-card', selector: 'stapps-simple-card',
templateUrl: 'simple-card.html', templateUrl: 'simple-card.html',
}) })
export class SimpleCardComponent { export class SimpleCardComponent {
areThings: boolean = false; /**
* TODO
*/
areThings = false;
/**
* TODO
*/
@Input() content: string | string[] | SCThing[]; @Input() content: string | string[] | SCThing[];
@Input() isMarkdown: boolean = false; /**
* TODO
*/
@Input() isMarkdown = false;
/**
* TODO
*/
@Input() title: string; @Input() title: string;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
isString(data: any): data is string { isString(data: any): data is string {
return typeof data === 'string'; return typeof data === 'string';
} }
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
isThing(something: any): something is SCThing { isThing(something: any): something is SCThing {
return isThing(something); return isThing(something);
} }
@@ -14,6 +14,9 @@
*/ */
import {Component} from '@angular/core'; import {Component} from '@angular/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-skeleton-list-item', selector: 'stapps-skeleton-list-item',
templateUrl: 'skeleton-list-item.html', templateUrl: 'skeleton-list-item.html',
@@ -14,6 +14,9 @@
*/ */
import {Component} from '@angular/core'; import {Component} from '@angular/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-skeleton-simple-card', selector: 'stapps-skeleton-simple-card',
templateUrl: 'skeleton-simple-card.html', templateUrl: 'skeleton-simple-card.html',
@@ -15,19 +15,32 @@
import {Component, Input, OnInit} from '@angular/core'; import {Component, Input, OnInit} from '@angular/core';
import {SCThings} from '@openstapps/core'; import {SCThings} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-data-list-item', selector: 'stapps-data-list-item',
styleUrls: ['data-list-item.scss'], styleUrls: ['data-list-item.scss'],
templateUrl: 'data-list-item.html', templateUrl: 'data-list-item.html',
}) })
export class DataListItem implements OnInit { export class DataListItem implements OnInit {
/**
* TODO
*/
@Input() item: SCThings; @Input() item: SCThings;
/**
* TODO
*/
constructor() { constructor() {
// noop // noop
// this.item is not available yet // this.item is not available yet
} }
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() { ngOnInit() {
// noop // noop
// this.item is available now - the template is loaded and compiled // this.item is available now - the template is loaded and compiled
@@ -19,24 +19,58 @@ import {Subject} from 'rxjs';
import {debounceTime, distinctUntilChanged} from 'rxjs/operators'; import {debounceTime, distinctUntilChanged} from 'rxjs/operators';
import {DataProvider} from '../data.provider'; import {DataProvider} from '../data.provider';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-data-list', selector: 'stapps-data-list',
templateUrl: 'data-list.html', templateUrl: 'data-list.html',
}) })
export class DataListComponent { export class DataListComponent {
/**
* TODO
*/
dataProvider: DataProvider; dataProvider: DataProvider;
/**
* TODO
*/
from = 0;
/**
* TODO
*/
items: SCThing[]; items: SCThing[];
selectedItem: any;
loaded: boolean = false;
size: number = 30; /**
from: number = 0; * TODO
*/
loaded = false;
/**
* TODO
*/
query: string; query: string;
/**
* TODO
*/
queryChanged: Subject<string> = new Subject<string>(); queryChanged: Subject<string> = new Subject<string>();
/**
* TODO
*/
selectedItem: any;
/**
* TODO
*/
size = 30;
/**
*
* @param alertController TODO
* @param dataProvider TODO
*/
constructor( constructor(
private alertController: AlertController, private readonly alertController: AlertController,
dataProvider: DataProvider, dataProvider: DataProvider,
) { ) {
this.dataProvider = dataProvider; this.dataProvider = dataProvider;
@@ -52,31 +86,41 @@ export class DataListComponent {
this.fetchItems(); this.fetchItems();
} }
/**
* TODO
*/
private async fetchItems(): Promise<any> { private async fetchItems(): Promise<any> {
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)
this.items = result.data; .then((result) => {
this.loaded = true; this.items = result.data;
}, async (err) => { this.loaded = true;
const alert: HTMLIonAlertElement = await this.alertController.create({ }, async (err) => {
buttons: ['Dismiss'], const alert: HTMLIonAlertElement = await this.alertController.create({
header: 'Error', buttons: ['Dismiss'],
subHeader: err.message, header: 'Error',
}); subHeader: err.message,
});
await alert.present(); await alert.present();
}); });
} }
/**
* TODO
*/
async loadMore(event: any): Promise<void> { async loadMore(event: any): Promise<void> {
this.from += this.size; this.from += this.size;
await this.fetchItems(); await this.fetchItems();
event.target.complete(); event.target.complete();
} }
/**
* TODO
*/
search(query: string) { search(query: string) {
this.queryChanged.next(query); this.queryChanged.next(query);
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018 StApps * Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -14,15 +14,18 @@
*/ */
import {HttpClient, HttpResponse} from '@angular/common/http'; import {HttpClient, HttpResponse} from '@angular/common/http';
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {HttpClientInterface} from '@openstapps/api/lib/httpClientInterface'; import {HttpClientInterface, HttpClientRequest} from '@openstapps/api/lib/http-client-interface';
import {HttpClientRequest} from '@openstapps/api/lib/httpClientInterface';
/** /**
* HttpClient that is based on angular's HttpClient (@TODO: move it to provider or independent package) * HttpClient that is based on angular's HttpClient (@TODO: move it to provider or independent package)
*/ */
@Injectable() @Injectable()
export class StAppsWebHttpClient implements HttpClientInterface { export class StAppsWebHttpClient implements HttpClientInterface {
constructor(private http: HttpClient) { /**
*
* @param http TODO
*/
constructor(private readonly http: HttpClient) {
} }
/** /**
@@ -33,7 +36,13 @@ export class StAppsWebHttpClient implements HttpClientInterface {
requestConfig: HttpClientRequest, requestConfig: HttpClientRequest,
): Promise<Response<TYPE_OF_BODY>> { ): Promise<Response<TYPE_OF_BODY>> {
const options: { const options: {
/**
* TODO
*/
[key: string]: any; [key: string]: any;
/**
* TODO
*/
observe: 'response'; observe: 'response';
} = { } = {
body: {}, body: {},
@@ -53,6 +62,7 @@ export class StAppsWebHttpClient implements HttpClientInterface {
const response: HttpResponse<TYPE_OF_BODY> = await this.http.request<TYPE_OF_BODY>( const response: HttpResponse<TYPE_OF_BODY> = await this.http.request<TYPE_OF_BODY>(
requestConfig.method || 'GET', requestConfig.url.toString(), options) requestConfig.method || 'GET', requestConfig.url.toString(), options)
.toPromise(); .toPromise();
return Object.assign(response, {statusCode: response.status, body: response.body || {}}); return Object.assign(response, {statusCode: response.status, body: response.body || {}});
} catch (err) { } catch (err) {
throw Error(err); throw Error(err);
@@ -64,6 +74,12 @@ export class StAppsWebHttpClient implements HttpClientInterface {
* Response with generic for the type of body that is returned from the request * Response with generic for the type of body that is returned from the request
*/ */
export interface Response<TYPE_OF_BODY> extends HttpResponse<TYPE_OF_BODY> { export interface Response<TYPE_OF_BODY> extends HttpResponse<TYPE_OF_BODY> {
/**
* TODO
*/
body: TYPE_OF_BODY; body: TYPE_OF_BODY;
/**
* TODO
*/
statusCode: number; statusCode: number;
} }
@@ -16,16 +16,34 @@ import {Component, Input} from '@angular/core';
import {SCArticle, SCThing, SCTranslations} from '@openstapps/core'; import {SCArticle, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core'; import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-article-detail-content', selector: 'stapps-article-detail-content',
templateUrl: 'article-detail-content.html', templateUrl: 'article-detail-content.html',
}) })
export class ArticleDetailContentComponent { export class ArticleDetailContentComponent {
/**
* TODO
*/
@Input() item: SCArticle; @Input() item: SCArticle;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>; @Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys; objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator; translator: SCThingTranslator;
/**
* TODO
*/
constructor() { constructor() {
this.translator = new SCThingTranslator(this.language, 'de'); this.translator = new SCThingTranslator(this.language);
} }
} }
@@ -16,14 +16,24 @@ import {Component, Input} from '@angular/core';
import {SCArticle} from '@openstapps/core'; import {SCArticle} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component'; import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-article-list-item', selector: 'stapps-article-list-item',
templateUrl: 'article-list-item.html', templateUrl: 'article-list-item.html',
}) })
export class ArticleListItem extends DataListItem { export class ArticleListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCArticle; @Input() item: SCArticle;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() { ngOnInit() {
// TODO: translation // TODO: translation
} }
@@ -16,16 +16,34 @@ import {Component, Input} from '@angular/core';
import {SCCatalog, SCThing, SCTranslations} from '@openstapps/core'; import {SCCatalog, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core'; import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-catalog-detail-content', selector: 'stapps-catalog-detail-content',
templateUrl: 'catalog-detail-content.html', templateUrl: 'catalog-detail-content.html',
}) })
export class CatalogDetailContentComponent { export class CatalogDetailContentComponent {
/**
* TODO
*/
@Input() item: SCCatalog; @Input() item: SCCatalog;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>; @Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys; objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator; translator: SCThingTranslator;
/**
* TODO
*/
constructor() { constructor() {
this.translator = new SCThingTranslator(this.language, 'de'); this.translator = new SCThingTranslator(this.language);
} }
} }
@@ -16,14 +16,24 @@ import {Component, Input} from '@angular/core';
import {SCCatalog} from '@openstapps/core'; import {SCCatalog} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component'; import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-catalog-list-item', selector: 'stapps-catalog-list-item',
templateUrl: 'catalog-list-item.html', templateUrl: 'catalog-list-item.html',
}) })
export class CatalogListItem extends DataListItem { export class CatalogListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCCatalog; @Input() item: SCCatalog;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() { ngOnInit() {
// TODO: translation // TODO: translation
} }
@@ -16,16 +16,34 @@ import {Component, Input} from '@angular/core';
import {SCDateSeries, SCThing, SCTranslations} from '@openstapps/core'; import {SCDateSeries, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core'; import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-date-series-detail-content', selector: 'stapps-date-series-detail-content',
templateUrl: 'date-series-detail-content.html', templateUrl: 'date-series-detail-content.html',
}) })
export class DateSeriesDetailContentComponent { export class DateSeriesDetailContentComponent {
/**
* TODO
*/
@Input() item: SCDateSeries; @Input() item: SCDateSeries;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>; @Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys; objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator; translator: SCThingTranslator;
/**
* TODO
*/
constructor() { constructor() {
this.translator = new SCThingTranslator(this.language, 'de'); this.translator = new SCThingTranslator(this.language);
} }
} }
@@ -16,14 +16,24 @@ import {Component, Input} from '@angular/core';
import {SCDateSeries} from '@openstapps/core'; import {SCDateSeries} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component'; import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-date-series-list-item', selector: 'stapps-date-series-list-item',
templateUrl: 'date-series-list-item.html', templateUrl: 'date-series-list-item.html',
}) })
export class DateSeriesListItem extends DataListItem { export class DateSeriesListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCDateSeries; @Input() item: SCDateSeries;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() { ngOnInit() {
// TODO: translation // TODO: translation
} }
@@ -16,16 +16,34 @@ import {Component, Input} from '@angular/core';
import {SCDish, SCThing, SCTranslations} from '@openstapps/core'; import {SCDish, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core'; import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-dish-detail-content', selector: 'stapps-dish-detail-content',
templateUrl: 'dish-detail-content.html', templateUrl: 'dish-detail-content.html',
}) })
export class DishDetailContentComponent { export class DishDetailContentComponent {
/**
* TODO
*/
@Input() item: SCDish; @Input() item: SCDish;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>; @Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys; objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator; translator: SCThingTranslator;
/**
* TODO
*/
constructor() { constructor() {
this.translator = new SCThingTranslator(this.language, 'de'); this.translator = new SCThingTranslator(this.language);
} }
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018 StApps * Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -17,12 +17,18 @@ import {SCDish} from '@openstapps/core';
// import {SettingsProvider} from '../../../settings/settings.provider'; // import {SettingsProvider} from '../../../settings/settings.provider';
import {DataListItem} from '../../list/data-list-item.component'; import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-dish-list-item', selector: 'stapps-dish-list-item',
templateUrl: 'dish-list-item.html', templateUrl: 'dish-list-item.html',
}) })
export class DishListItem extends DataListItem { export class DishListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCDish; @Input() item: SCDish;
// settingsProvider: SettingsProvider; // settingsProvider: SettingsProvider;
@@ -31,6 +37,10 @@ export class DishListItem extends DataListItem {
// this.settingsProvider = settingsProvider; // this.settingsProvider = settingsProvider;
// } // }
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() { ngOnInit() {
// TODO: translation... // TODO: translation...
} }
@@ -16,16 +16,34 @@ import {Component, Input} from '@angular/core';
import {SCAcademicEvent, SCSportCourse, SCThing, SCTranslations} from '@openstapps/core'; import {SCAcademicEvent, SCSportCourse, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core'; import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-event-detail-content', selector: 'stapps-event-detail-content',
templateUrl: 'event-detail-content.html', templateUrl: 'event-detail-content.html',
}) })
export class EventDetailContentComponent { export class EventDetailContentComponent {
/**
* TODO
*/
@Input() item: SCAcademicEvent | SCSportCourse; @Input() item: SCAcademicEvent | SCSportCourse;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>; @Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys; objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator; translator: SCThingTranslator;
/**
* TODO
*/
constructor() { constructor() {
this.translator = new SCThingTranslator(this.language, 'de'); this.translator = new SCThingTranslator(this.language);
} }
} }
@@ -16,13 +16,23 @@ import {Component, Input} from '@angular/core';
import {SCAcademicEvent, SCSportCourse} from '@openstapps/core'; import {SCAcademicEvent, SCSportCourse} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component'; import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-event-list-item', selector: 'stapps-event-list-item',
templateUrl: 'event-list-item.html', templateUrl: 'event-list-item.html',
}) })
export class EventListItemComponent extends DataListItem { export class EventListItemComponent extends DataListItem {
/**
* TODO
*/
@Input() item: SCAcademicEvent | SCSportCourse; @Input() item: SCAcademicEvent | SCSportCourse;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() { ngOnInit() {
// TODO: translation // TODO: translation
} }
@@ -16,16 +16,34 @@ import {Component, Input} from '@angular/core';
import {SCFavorite, SCThing, SCTranslations} from '@openstapps/core'; import {SCFavorite, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core'; import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-favorite-detail-content', selector: 'stapps-favorite-detail-content',
templateUrl: 'favorite-detail-content.html', templateUrl: 'favorite-detail-content.html',
}) })
export class FavoriteDetailContentComponent { export class FavoriteDetailContentComponent {
/**
* TODO
*/
@Input() item: SCFavorite; @Input() item: SCFavorite;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>; @Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys; objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator; translator: SCThingTranslator;
/**
* TODO
*/
constructor() { constructor() {
this.translator = new SCThingTranslator(this.language, 'de'); this.translator = new SCThingTranslator(this.language);
} }
} }
@@ -16,14 +16,24 @@ import {Component, Input} from '@angular/core';
import {SCFavorite} from '@openstapps/core'; import {SCFavorite} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component'; import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-favorite-list-item', selector: 'stapps-favorite-list-item',
templateUrl: 'favorite-list-item.html', templateUrl: 'favorite-list-item.html',
}) })
export class FavoriteListItem extends DataListItem { export class FavoriteListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCFavorite; @Input() item: SCFavorite;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() { ngOnInit() {
// TODO: translation // TODO: translation
} }
@@ -16,16 +16,34 @@ import {Component, Input} from '@angular/core';
import {SCMessage, SCThing, SCTranslations} from '@openstapps/core'; import {SCMessage, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core'; import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-message-detail-content', selector: 'stapps-message-detail-content',
templateUrl: 'message-detail-content.html', templateUrl: 'message-detail-content.html',
}) })
export class MessageDetailContentComponent { export class MessageDetailContentComponent {
/**
* TODO
*/
@Input() item: SCMessage; @Input() item: SCMessage;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>; @Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys; objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator; translator: SCThingTranslator;
/**
* TODO
*/
constructor() { constructor() {
this.translator = new SCThingTranslator(this.language, 'de'); this.translator = new SCThingTranslator(this.language);
} }
} }
@@ -1,4 +1,4 @@
<stapps-simple-card [title]="'Content'" [content]="item.message"></stapps-simple-card> <stapps-simple-card [title]="'Content'" [content]="item.messageBody"></stapps-simple-card>
<stapps-simple-card [title]="'Audiences'" [content]="item.audiences"></stapps-simple-card> <stapps-simple-card [title]="'Audiences'" [content]="item.audiences"></stapps-simple-card>
<stapps-simple-card *ngIf="item.datePublished" [title]="'Published'" [content]="item.datePublished | amDateFormat:'DD. MMM YYYY'"></stapps-simple-card> <stapps-simple-card *ngIf="item.datePublished" [title]="'Published'" [content]="item.datePublished | amDateFormat:'DD. MMM YYYY'"></stapps-simple-card>
<stapps-simple-card *ngIf="item.authors" [title]="'Author(s)'" [content]="item.authors"></stapps-simple-card> <stapps-simple-card *ngIf="item.authors" [title]="'Author(s)'" [content]="item.authors"></stapps-simple-card>
@@ -16,14 +16,24 @@ import {Component, Input} from '@angular/core';
import {SCMessage} from '@openstapps/core'; import {SCMessage} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component'; import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-message-list-item', selector: 'stapps-message-list-item',
templateUrl: 'message-list-item.html', templateUrl: 'message-list-item.html',
}) })
export class MessageListItem extends DataListItem { export class MessageListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCMessage; @Input() item: SCMessage;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() { ngOnInit() {
// TODO: translation // TODO: translation
} }
@@ -2,8 +2,8 @@
<ion-row> <ion-row>
<ion-col> <ion-col>
<h2 class="name">{{item.name}}</h2> <h2 class="name">{{item.name}}</h2>
<p *ngIf="item.message"> <p *ngIf="item.messageBody">
<stapps-long-inline-text [text]="item.message" [size]="80"></stapps-long-inline-text> <stapps-long-inline-text [text]="item.messageBody" [size]="80"></stapps-long-inline-text>
</p> </p>
<ion-note>{{item.type}}</ion-note> <ion-note>{{item.type}}</ion-note>
</ion-col> </ion-col>
@@ -16,16 +16,35 @@ import {Component, Input} from '@angular/core';
import {SCOrganization, SCThing, SCTranslations} from '@openstapps/core'; import {SCOrganization, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core'; import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-organization-detail-content', selector: 'stapps-organization-detail-content',
templateUrl: 'organization-detail-content.html', templateUrl: 'organization-detail-content.html',
}) })
export class OrganizationDetailContentComponent { export class OrganizationDetailContentComponent {
/**
* TODO
*/
@Input() item: SCOrganization; @Input() item: SCOrganization;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>; @Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys; objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator; translator: SCThingTranslator;
/**
* TODO
*/
constructor() { constructor() {
this.translator = new SCThingTranslator(this.language, 'de'); this.translator = new SCThingTranslator(this.language);
} }
} }
@@ -16,14 +16,24 @@ import {Component, Input} from '@angular/core';
import {SCOrganization} from '@openstapps/core'; import {SCOrganization} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component'; import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-organization-list-item', selector: 'stapps-organization-list-item',
templateUrl: 'organization-list-item.html', templateUrl: 'organization-list-item.html',
}) })
export class OrganizationListItem extends DataListItem { export class OrganizationListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCOrganization; @Input() item: SCOrganization;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() { ngOnInit() {
// TODO: translation // TODO: translation
} }
@@ -16,16 +16,35 @@ import {Component, Input} from '@angular/core';
import {SCPerson, SCThing, SCTranslations} from '@openstapps/core'; import {SCPerson, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core'; import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-person-detail-content', selector: 'stapps-person-detail-content',
templateUrl: 'person-detail-content.html', templateUrl: 'person-detail-content.html',
}) })
export class PersonDetailContentComponent { export class PersonDetailContentComponent {
/**
* TODO
*/
@Input() item: SCPerson; @Input() item: SCPerson;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>; @Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys; objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator; translator: SCThingTranslator;
/**
* TODO
*/
constructor() { constructor() {
this.translator = new SCThingTranslator(this.language, 'de'); this.translator = new SCThingTranslator(this.language);
} }
} }
@@ -16,14 +16,24 @@ import {Component, Input} from '@angular/core';
import {SCPerson} from '@openstapps/core'; import {SCPerson} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component'; import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-person-list-item', selector: 'stapps-person-list-item',
templateUrl: 'person-list-item.html', templateUrl: 'person-list-item.html',
}) })
export class PersonListItem extends DataListItem { export class PersonListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCPerson; @Input() item: SCPerson;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() { ngOnInit() {
// TODO: translation // TODO: translation
} }
@@ -16,16 +16,35 @@ import {Component, Input} from '@angular/core';
import {SCBuilding, SCFloor, SCPointOfInterest, SCRoom, SCThing, SCTranslations} from '@openstapps/core'; import {SCBuilding, SCFloor, SCPointOfInterest, SCRoom, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core'; import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-place-detail-content', selector: 'stapps-place-detail-content',
templateUrl: 'place-detail-content.html', templateUrl: 'place-detail-content.html',
}) })
export class PlaceDetailContentComponent { export class PlaceDetailContentComponent {
/**
* TODO
*/
@Input() item: SCBuilding | SCRoom | SCPointOfInterest | SCFloor; @Input() item: SCBuilding | SCRoom | SCPointOfInterest | SCFloor;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>; @Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys; objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator; translator: SCThingTranslator;
/**
* TODO
*/
constructor() { constructor() {
this.translator = new SCThingTranslator(this.language, 'de'); this.translator = new SCThingTranslator(this.language);
} }
} }
@@ -16,14 +16,24 @@ import {Component, Input} from '@angular/core';
import {SCBuilding, SCFloor, SCPointOfInterest, SCRoom} from '@openstapps/core'; import {SCBuilding, SCFloor, SCPointOfInterest, SCRoom} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component'; import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-place-list-item', selector: 'stapps-place-list-item',
templateUrl: 'place-list-item.html', templateUrl: 'place-list-item.html',
}) })
export class PlaceListItem extends DataListItem { export class PlaceListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCBuilding | SCRoom | SCPointOfInterest | SCFloor; @Input() item: SCBuilding | SCRoom | SCPointOfInterest | SCFloor;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() { ngOnInit() {
// TODO: translation // TODO: translation
} }
@@ -16,16 +16,35 @@ import {Component, Input} from '@angular/core';
import {SCSemester, SCThing, SCTranslations} from '@openstapps/core'; import {SCSemester, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core'; import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-semester-detail-content', selector: 'stapps-semester-detail-content',
templateUrl: 'semester-detail-content.html', templateUrl: 'semester-detail-content.html',
}) })
export class SemesterDetailContentComponent { export class SemesterDetailContentComponent {
/**
* TODO
*/
@Input() item: SCSemester; @Input() item: SCSemester;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>; @Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys; objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator; translator: SCThingTranslator;
/**
* TODO
*/
constructor() { constructor() {
this.translator = new SCThingTranslator(this.language, 'de'); this.translator = new SCThingTranslator(this.language);
} }
} }
@@ -16,14 +16,23 @@ import {Component, Input} from '@angular/core';
import {SCSemester} from '@openstapps/core'; import {SCSemester} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component'; import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-semester-list-item', selector: 'stapps-semester-list-item',
templateUrl: 'semester-list-item.html', templateUrl: 'semester-list-item.html',
}) })
export class SemesterListItem extends DataListItem { export class SemesterListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCSemester; @Input() item: SCSemester;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() { ngOnInit() {
// TODO: translation // TODO: translation
} }
@@ -16,16 +16,35 @@ import {Component, Input} from '@angular/core';
import {SCThing, SCTranslations, SCVideo} from '@openstapps/core'; import {SCThing, SCTranslations, SCVideo} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core'; import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-video-detail-content', selector: 'stapps-video-detail-content',
templateUrl: 'video-detail-content.html', templateUrl: 'video-detail-content.html',
}) })
export class VideoDetailContentComponent { export class VideoDetailContentComponent {
/**
* TODO
*/
@Input() item: SCVideo; @Input() item: SCVideo;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>; @Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys; objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator; translator: SCThingTranslator;
/**
* TODO
*/
constructor() { constructor() {
this.translator = new SCThingTranslator(this.language, 'de'); this.translator = new SCThingTranslator(this.language);
} }
} }
@@ -16,14 +16,23 @@ import {Component, Input} from '@angular/core';
import {SCVideo} from '@openstapps/core'; import {SCVideo} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component'; import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-video-list-item', selector: 'stapps-video-list-item',
templateUrl: 'video-list-item.html', templateUrl: 'video-list-item.html',
}) })
export class VideoListItem extends DataListItem { export class VideoListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCVideo; @Input() item: SCVideo;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() { ngOnInit() {
// TODO: translation // TODO: translation
} }
+8 -5
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018 StApps * Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -18,20 +18,23 @@ import {RouterModule} from '@angular/router';
import {IonicModule} from '@ionic/angular'; import {IonicModule} from '@ionic/angular';
import {NavigationComponent} from './navigation/navigation.component'; import {NavigationComponent} from './navigation/navigation.component';
/**
* TODO
*/
@NgModule({ @NgModule({
declarations: [ declarations: [
NavigationComponent, NavigationComponent,
], ],
entryComponents: [ entryComponents: [
NavigationComponent NavigationComponent,
], ],
exports: [ exports: [
NavigationComponent NavigationComponent,
], ],
imports: [ imports: [
IonicModule.forRoot(), IonicModule.forRoot(),
CommonModule, CommonModule,
RouterModule RouterModule,
] ],
}) })
export class MenuModule {} export class MenuModule {}
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018 StApps * Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -26,6 +26,9 @@ import {Component} from '@angular/core';
templateUrl: 'navigation.html', templateUrl: 'navigation.html',
}) })
export class NavigationComponent { export class NavigationComponent {
/**
* TODO
*/
public pages = [ public pages = [
{title: 'Search', url: '/search', icon: 'search'}, {title: 'Search', url: '/search', icon: 'search'},
{title: 'Settings', url: '/settings', icon: 'settings'}, {title: 'Settings', url: '/settings', icon: 'settings'},
@@ -35,6 +38,10 @@ export class NavigationComponent {
// Nothing yet. // Nothing yet.
} }
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ionViewDidLoad() { ionViewDidLoad() {
// console.log('ionViewDidLoad MenuPage'); // console.log('ionViewDidLoad MenuPage');
} }
@@ -17,34 +17,56 @@ import {AlertController} from '@ionic/angular';
import {LangChangeEvent, TranslateService} from '@ngx-translate/core'; import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
import { import {
SCSetting, SCSetting,
SCSettingValue,
SCSettingValues,
SCThingTranslator, SCThingTranslator,
SCTranslations, SCTranslations,
} from '@openstapps/core'; } from '@openstapps/core';
import {Logger} from '@openstapps/logger';
import {SettingsProvider} from '../settings.provider'; import {SettingsProvider} from '../settings.provider';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-settings-item', selector: 'stapps-settings-item',
templateUrl: 'settings-item.html', templateUrl: 'settings-item.html',
}) })
export class SettingsItemComponent { export class SettingsItemComponent {
/**
* TODO
*/
isVisible = true; isVisible = true;
// limit to languages that are available in StApps Core /**
* TODO
*
* limit to languages that are available in StApps Core
*/
language: keyof SCTranslations<any>; language: keyof SCTranslations<any>;
logger = new Logger(); /**
* TODO
*/
@Input() setting: SCSetting; @Input() setting: SCSetting;
/**
* TODO
*/
translator: SCThingTranslator; translator: SCThingTranslator;
constructor(private alertCtrl: AlertController, /**
private translateService: TranslateService, *
private settingsProvider: SettingsProvider) { * @param alertCtrl TODO
* @param translateService TODO
* @param settingsProvider TODO
*/
constructor(private readonly alertCtrl: AlertController,
private readonly translateService: TranslateService,
private readonly settingsProvider: SettingsProvider) {
this.language = translateService.currentLang as keyof SCTranslations<any>; this.language = translateService.currentLang as keyof SCTranslations<any>;
this.translator = new SCThingTranslator(this.language, 'de'); this.translator = new SCThingTranslator(this.language);
translateService.onLangChange.subscribe((event: LangChangeEvent) => { translateService.onLangChange.subscribe((event: LangChangeEvent) => {
this.isVisible = false; this.isVisible = false;
this.language = event.lang as keyof SCTranslations<any>; this.language = event.lang as keyof SCTranslations<any>;
this.translator = new SCThingTranslator(this.language, 'de'); this.translator = new SCThingTranslator(this.language);
// TODO: Issue #53 check workaround for selected 'select option' not updating translation // TODO: Issue #53 check workaround for selected 'select option' not updating translation
setTimeout(() => this.isVisible = true); setTimeout(() => this.isVisible = true);
}); });
@@ -58,7 +80,7 @@ export class SettingsItemComponent {
const permissionGranted = await this.settingsProvider.checkGeoLocationPermission(); const permissionGranted = await this.settingsProvider.checkGeoLocationPermission();
if (!permissionGranted) { if (!permissionGranted) {
// revert setting value // revert setting value
this.setting.input.value = false; this.setting.value = false;
await this.presentGeoLocationAlert(); await this.presentGeoLocationAlert();
} }
} }
@@ -67,8 +89,10 @@ export class SettingsItemComponent {
* Shows alert with error message on denied user permission or disabled location services * Shows alert with error message on denied user permission or disabled location services
*/ */
private async presentGeoLocationAlert() { private async presentGeoLocationAlert() {
const title = await this.translateService.get('settings.geoLocation.permission_denied_title').toPromise(); const title = await this.translateService.get('settings.geoLocation.permission_denied_title')
const message = await this.translateService.get('settings.geoLocation.permission_denied_message').toPromise(); .toPromise();
const message = await this.translateService.get('settings.geoLocation.permission_denied_message')
.toPromise();
await this.presentAlert(title, message); await this.presentAlert(title, message);
} }
@@ -91,29 +115,34 @@ export class SettingsItemComponent {
* Handles value changes of the setting * Handles value changes of the setting
*/ */
async settingChanged(): Promise<void> { async settingChanged(): Promise<void> {
if (typeof this.setting.input.value !== 'undefined' if (typeof this.setting.value !== 'undefined'
&& SettingsProvider.validateValue(this.setting, this.setting.input.value)) { && SettingsProvider.validateValue(this.setting, this.setting.value)) {
// handle general settings, with special actions // handle general settings, with special actions
switch (this.setting.name) { switch (this.setting.name) {
case 'language': case 'language':
this.translateService.use(this.setting.input.value.toString()); this.translateService.use(this.setting.value.toString());
break; break;
case 'geoLocation': case 'geoLocation':
if (this.setting.input.value) { if (this.setting.value) {
await this.checkGeoLocationPermission(); await this.checkGeoLocationPermission();
} }
break; break;
default: default:
} }
await this.settingsProvider await this.settingsProvider
.setSettingValue(this.setting.categories[0], this.setting.name, this.setting.input.value); .setSettingValue(this.setting.categories[0], this.setting.name, this.setting.value);
} else { } else {
// reset setting // reset setting
this.setting.input.value = this.setting.value =
await this.settingsProvider.getValue(this.setting.categories[0], this.setting.name); await this.settingsProvider
.getValue(this.setting.categories[0], this.setting.name) as (SCSettingValue | SCSettingValues);
} }
} }
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
typeOf(val: any) { typeOf(val: any) {
return typeof (val); return typeof (val);
} }
@@ -5,27 +5,27 @@
<ion-card-content> <ion-card-content>
<ion-note>{{ translator.translate(setting).description() }}</ion-note> <ion-note>{{ translator.translate(setting).description() }}</ion-note>
<div [ngSwitch]="setting.input.inputType" *ngIf="isVisible" > <div [ngSwitch]="setting.inputType" *ngIf="isVisible" >
<ion-item *ngSwitchCase="'number'"> <ion-item *ngSwitchCase="'number'">
<ion-label></ion-label> <ion-label></ion-label>
<ion-input type='number' [(ngModel)]="setting.input.value" value={{setting.input.value}} (ionChange)="settingChanged()"></ion-input> <ion-input type='number' [(ngModel)]="setting.value" value={{setting.value}} (ionChange)="settingChanged()"></ion-input>
</ion-item> </ion-item>
<ion-item *ngSwitchCase="'text'"> <ion-item *ngSwitchCase="'text'">
<ion-label></ion-label> <ion-label></ion-label>
<ion-input type="text" [(ngModel)]="setting.input.value" value={{setting.input.value}} (ionChange)="settingChanged()"></ion-input> <ion-input type="text" [(ngModel)]="setting.value" value={{setting.value}} (ionChange)="settingChanged()"></ion-input>
</ion-item> </ion-item>
<ion-item *ngSwitchCase="'password'"> <ion-item *ngSwitchCase="'password'">
<ion-label></ion-label> <ion-label></ion-label>
<ion-input type="password" [(ngModel)]="setting.input.value" value={{setting.input.value}} (ionChange)="settingChanged()"></ion-input> <ion-input type="password" [(ngModel)]="setting.value" value={{setting.value}} (ionChange)="settingChanged()"></ion-input>
</ion-item> </ion-item>
<ion-item *ngSwitchCase="'singleChoice'"> <ion-item *ngSwitchCase="'singleChoice'">
<ion-label></ion-label> <ion-label></ion-label>
<ion-toggle *ngIf="typeOf(setting.input.defaultValue) === 'boolean'" [(ngModel)]="setting.input.value" (ionChange)="settingChanged()"></ion-toggle> <ion-toggle *ngIf="typeOf(setting.defaultValue) === 'boolean'" [(ngModel)]="setting.value" (ionChange)="settingChanged()"></ion-toggle>
<ion-select *ngIf="typeOf(setting.input.defaultValue) !== 'boolean'" interface="popover" [(ngModel)]="setting.input.value" (ionChange)="settingChanged()"> <ion-select *ngIf="typeOf(setting.defaultValue) !== 'boolean'" interface="popover" [(ngModel)]="setting.value" (ionChange)="settingChanged()">
<ion-select-option *ngFor="let val of setting.input.values" [value]="val"> <ion-select-option *ngFor="let val of setting.values" [value]="val">
<div *ngIf="typeOf(val) !== 'number'">{{ val }}</div> <div *ngIf="typeOf(val) !== 'number'">{{ val }}</div>
<div *ngIf="typeOf(val) === 'number'">{{ val }}</div> <div *ngIf="typeOf(val) === 'number'">{{ val }}</div>
</ion-select-option> </ion-select-option>
@@ -34,8 +34,8 @@
<ion-item *ngSwitchCase="'multipleChoice'"> <ion-item *ngSwitchCase="'multipleChoice'">
<ion-label></ion-label> <ion-label></ion-label>
<ion-select [(ngModel)]="setting.input.value" multiple="true" (ionChange)="settingChanged()"> <ion-select [(ngModel)]="setting.value" multiple="true" (ionChange)="settingChanged()">
<ion-select-option *ngFor="let val of setting.input.values" [value]="val"> <ion-select-option *ngFor="let val of setting.values" [value]="val">
<div *ngIf="typeOf(val) !== 'number'">{{ val }}</div> <div *ngIf="typeOf(val) !== 'number'">{{ val }}</div>
<div *ngIf="typeOf(val) === 'number'">{{ val }}</div> <div *ngIf="typeOf(val) === 'number'">{{ val }}</div>
</ion-select-option> </ion-select-option>
@@ -18,29 +18,58 @@ import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
import {SCSettingMeta, SCThingTranslator, SCTranslations} from '@openstapps/core'; import {SCSettingMeta, SCThingTranslator, SCTranslations} from '@openstapps/core';
import {SettingsCache, SettingsProvider} from '../settings.provider'; import {SettingsCache, SettingsProvider} from '../settings.provider';
/**
* TODO
*/
@Component({ @Component({
selector: 'stapps-settings-page', selector: 'stapps-settings-page',
templateUrl: 'settings-page.html', templateUrl: 'settings-page.html',
}) })
export class SettingsPageComponent { export class SettingsPageComponent {
/**
* Order of the categories
*/
categoriesOrder: string[]; categoriesOrder: string[];
// limit to languages that are available in StApps Core /**
* Possible languages to be used for translation
*
* limit to languages that are available in StApps Core
*/
language: keyof SCTranslations<any>; language: keyof SCTranslations<any>;
/**
* Meta information about settings
*/
meta = SCSettingMeta; meta = SCSettingMeta;
/**
* TODO
*/
objectKeys = Object.keys; objectKeys = Object.keys;
/**
* TODO
*/
settingsCache: SettingsCache; settingsCache: SettingsCache;
/**
* TODO
*/
translator: SCThingTranslator; translator: SCThingTranslator;
constructor(private alertController: AlertController, /**
private settingsProvider: SettingsProvider, *
private toastController: ToastController, * @param alertController TODO
private translateService: TranslateService) { * @param settingsProvider TODO
* @param toastController TODO
* @param translateService TODO
*/
constructor(private readonly alertController: AlertController,
private readonly settingsProvider: SettingsProvider,
private readonly toastController: ToastController,
private readonly translateService: TranslateService) {
this.language = translateService.currentLang as keyof SCTranslations<any>; this.language = translateService.currentLang as keyof SCTranslations<any>;
this.translator = new SCThingTranslator(this.language, 'de'); this.translator = new SCThingTranslator(this.language);
translateService.onLangChange.subscribe((event: LangChangeEvent) => { translateService.onLangChange.subscribe((event: LangChangeEvent) => {
this.language = event.lang as keyof SCTranslations<any>; this.language = event.lang as keyof SCTranslations<any>;
this.translator = new SCThingTranslator(this.language, 'de'); this.translator = new SCThingTranslator(this.language);
}); });
this.settingsCache = {}; this.settingsCache = {};
this.categoriesOrder = settingsProvider.getCategoriesOrder(); this.categoriesOrder = settingsProvider.getCategoriesOrder();
@@ -66,6 +95,9 @@ export class SettingsPageComponent {
this.settingsCache = await this.settingsProvider.getCache(); this.settingsCache = await this.settingsProvider.getCache();
} }
/**
* Component initialize method
*/
async ngOnInit() { async ngOnInit() {
await this.loadSettings(); await this.loadSettings();
} }
@@ -74,10 +106,14 @@ export class SettingsPageComponent {
* Presents an alert to the user to reset settings to default values * Presents an alert to the user to reset settings to default values
*/ */
async presentResetAlert() { async presentResetAlert() {
const cancelText = await this.translateService.get('settings.resetAlert.buttonCancel').toPromise(); const cancelText = await this.translateService.get('settings.resetAlert.buttonCancel')
const yesText = await this.translateService.get('settings.resetAlert.buttonYes').toPromise(); .toPromise();
const title = await this.translateService.get('settings.resetAlert.title').toPromise(); const yesText = await this.translateService.get('settings.resetAlert.buttonYes')
const message = await this.translateService.get('settings.resetAlert.message').toPromise(); .toPromise();
const title = await this.translateService.get('settings.resetAlert.title')
.toPromise();
const message = await this.translateService.get('settings.resetAlert.message')
.toPromise();
const alert = await this.alertController.create({ const alert = await this.alertController.create({
buttons: [ buttons: [
@@ -12,7 +12,7 @@
<ion-list *ngFor="let categoryKey of categoriesOrder "> <ion-list *ngFor="let categoryKey of categoriesOrder ">
<div *ngIf="objectKeys(settingsCache).includes(categoryKey)"> <div *ngIf="objectKeys(settingsCache).includes(categoryKey)">
<ion-item-divider> <ion-item-divider>
<h5>{{translator.translate(settingsCache[categoryKey].settings[objectKeys(settingsCache[categoryKey].settings)[0]]).categories()[0]}} <h5>{{ settingsCache[categoryKey].settings[objectKeys(settingsCache[categoryKey].settings)[0]].translations[language].categories[0]}}
</h5> </h5>
</ion-item-divider> </ion-item-divider>
<stapps-settings-item *ngFor="let settingKeys of objectKeys(settingsCache[categoryKey].settings)" [setting]="settingsCache[categoryKey].settings[settingKeys]"></stapps-settings-item> <stapps-settings-item *ngFor="let settingKeys of objectKeys(settingsCache[categoryKey].settings)" [setting]="settingsCache[categoryKey].settings[settingKeys]"></stapps-settings-item>
@@ -29,6 +29,9 @@ const settingsRoutes: Routes = [
{path: 'settings', component: SettingsPageComponent}, {path: 'settings', component: SettingsPageComponent},
]; ];
/**
* Settings Module
*/
@NgModule({ @NgModule({
declarations: [ declarations: [
SettingsPageComponent, SettingsPageComponent,
@@ -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 {TestBed} from '@angular/core/testing'; import {TestBed} from '@angular/core/testing';
import {SCSetting, SCThingOriginType, SCThingType} from '@openstapps/core'; import {SCSetting, SCThingOriginType, SCThingType, SCSettingInputType} from '@openstapps/core';
import {ConfigProvider} from '../config/config.provider'; import {ConfigProvider} from '../config/config.provider';
import {StorageProvider} from '../storage/storage.provider'; import {StorageProvider} from '../storage/storage.provider';
import {SettingsProvider, SettingValuesContainer, STORAGE_KEY_SETTING_VALUES} from './settings.provider'; import {SettingsProvider, SettingValuesContainer, STORAGE_KEY_SETTING_VALUES} from './settings.provider';
@@ -53,14 +53,14 @@ describe('SettingsProvider', () => {
await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0]))); await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0])));
const setting: SCSetting = await settingsProvider const setting: SCSetting = await settingsProvider
.getSetting(CONFIG_SETTINGS_MOCK[0].categories[0], CONFIG_SETTINGS_MOCK[0].name); .getSetting(CONFIG_SETTINGS_MOCK[0].categories[0], CONFIG_SETTINGS_MOCK[0].name);
await expect(setting.input.value).toBeDefined(); await expect(setting.value).toBeDefined();
}); });
it('should provide and get settings value', async () => { it('should provide and get settings value', async () => {
await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0]))); await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0])));
const value = await settingsProvider const value = await settingsProvider
.getValue(CONFIG_SETTINGS_MOCK[0].categories[0], CONFIG_SETTINGS_MOCK[0].name); .getValue(CONFIG_SETTINGS_MOCK[0].categories[0], CONFIG_SETTINGS_MOCK[0].name);
await expect(value).toEqual(CONFIG_SETTINGS_MOCK[0].input.defaultValue); await expect(value).toEqual(CONFIG_SETTINGS_MOCK[0].defaultValue);
}); });
it('should get persisted setting value', async () => { it('should get persisted setting value', async () => {
@@ -79,7 +79,7 @@ describe('SettingsProvider', () => {
storageProviderSpy.get.and.returnValue(Promise.resolve([])); storageProviderSpy.get.and.returnValue(Promise.resolve([]));
const value = await settingsProvider const value = await settingsProvider
.getValue(CONFIG_SETTINGS_MOCK[3].categories[0], CONFIG_SETTINGS_MOCK[3].name); .getValue(CONFIG_SETTINGS_MOCK[3].categories[0], CONFIG_SETTINGS_MOCK[3].name);
await expect(value).toEqual(CONFIG_SETTINGS_MOCK[3].input.defaultValue); await expect(value).toEqual(CONFIG_SETTINGS_MOCK[3].defaultValue);
}); });
it('should keep persisted setting values from settings that are not contained in loaded config', async () => { it('should keep persisted setting values from settings that are not contained in loaded config', async () => {
@@ -108,10 +108,10 @@ describe('SettingsProvider', () => {
const name = CONFIG_SETTINGS_MOCK[0].name; const name = CONFIG_SETTINGS_MOCK[0].name;
await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0]))); await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0])));
const settings = await settingsProvider.getCache(); const settings = await settingsProvider.getCache();
settings[category].settings[name].input.value = 'testValue'; settings[category].settings[name].value = 'testValue';
// cached setting value should still be defaultValue // cached setting value should still be defaultValue
await expect((await settingsProvider.getValue(category, name))) await expect((await settingsProvider.getValue(category, name)))
.toEqual(CONFIG_SETTINGS_MOCK[0].input.defaultValue); .toEqual(CONFIG_SETTINGS_MOCK[0].defaultValue);
}); });
it('should call storage put on setSettingValue', async () => { it('should call storage put on setSettingValue', async () => {
@@ -134,7 +134,7 @@ describe('SettingsProvider', () => {
await settingsProvider.resetDefault(); await settingsProvider.resetDefault();
const value = await settingsProvider const value = await settingsProvider
.getValue(CONFIG_SETTINGS_MOCK[0].categories[0], CONFIG_SETTINGS_MOCK[0].name); .getValue(CONFIG_SETTINGS_MOCK[0].categories[0], CONFIG_SETTINGS_MOCK[0].name);
await expect(value).toEqual(CONFIG_SETTINGS_MOCK[0].input.defaultValue); await expect(value).toEqual(CONFIG_SETTINGS_MOCK[0].defaultValue);
}); });
it('should validate wrong values for inputType text', async () => { it('should validate wrong values for inputType text', async () => {
@@ -172,7 +172,7 @@ describe('SettingsProvider', () => {
await testValue(CONFIG_SETTINGS_MOCK[6], ''); await testValue(CONFIG_SETTINGS_MOCK[6], '');
await testValue(CONFIG_SETTINGS_MOCK[6], 123456); await testValue(CONFIG_SETTINGS_MOCK[6], 123456);
await testValue(CONFIG_SETTINGS_MOCK[6], false); await testValue(CONFIG_SETTINGS_MOCK[6], false);
await testValue(CONFIG_SETTINGS_MOCK[6], [1, 2, 3, 4]); await testValue(CONFIG_SETTINGS_MOCK[6], [1, 9]);
}); });
async function testValue(setting: SCSetting, value: any) { async function testValue(setting: SCSetting, value: any) {
@@ -192,10 +192,8 @@ describe('SettingsProvider', () => {
const CONFIG_SETTINGS_MOCK: SCSetting[] = [ const CONFIG_SETTINGS_MOCK: SCSetting[] = [
{ {
categories: ['credentials'], categories: ['credentials'],
input: { defaultValue: '',
defaultValue: '', inputType: SCSettingInputType.Text,
inputType: 'text',
},
name: 'username', name: 'username',
order: 0, order: 0,
origin: { origin: {
@@ -219,10 +217,8 @@ describe('SettingsProvider', () => {
{ {
categories: ['credentials'], categories: ['credentials'],
description: '', description: '',
input: { defaultValue: '',
defaultValue: '', inputType: SCSettingInputType.Password,
inputType: 'password',
},
name: 'password', name: 'password',
order: 1, order: 1,
origin: { origin: {
@@ -246,10 +242,8 @@ describe('SettingsProvider', () => {
{ {
categories: ['profile'], categories: ['profile'],
description: '', description: '',
input: { defaultValue: 0,
defaultValue: 0, inputType: SCSettingInputType.Number,
inputType: 'number',
},
name: 'age', name: 'age',
order: 0, order: 0,
origin: { origin: {
@@ -273,11 +267,8 @@ describe('SettingsProvider', () => {
{ {
categories: ['profile'], categories: ['profile'],
description: '', description: '',
input: { defaultValue: 'student',
defaultValue: 'student', inputType: SCSettingInputType.SingleChoice,
inputType: 'singleChoice',
values: ['student', 'employee', 'guest'],
},
name: 'group', name: 'group',
order: 1, order: 1,
origin: { origin: {
@@ -301,15 +292,13 @@ describe('SettingsProvider', () => {
}, },
type: SCThingType.Setting, type: SCThingType.Setting,
uid: '', uid: '',
values: ['student', 'employee', 'guest'],
}, },
{ {
categories: ['profile'], categories: ['profile'],
description: '', description: '',
input: { defaultValue: 'en',
defaultValue: 'en', inputType: SCSettingInputType.SingleChoice,
inputType: 'singleChoice',
values: ['en', 'de'],
},
name: 'language', name: 'language',
order: 0, order: 0,
origin: { origin: {
@@ -331,15 +320,13 @@ describe('SettingsProvider', () => {
}, },
type: SCThingType.Setting, type: SCThingType.Setting,
uid: '', uid: '',
values: ['en', 'de'],
}, },
{ {
categories: ['privacy'], categories: ['privacy'],
description: '', description: '',
input: { defaultValue: false,
defaultValue: false, inputType: SCSettingInputType.SingleChoice,
inputType: 'singleChoice',
values: [true, false],
},
name: 'geoLocation', name: 'geoLocation',
order: 0, order: 0,
origin: { origin: {
@@ -363,15 +350,13 @@ describe('SettingsProvider', () => {
}, },
type: SCThingType.Setting, type: SCThingType.Setting,
uid: '', uid: '',
values: [true, false],
}, },
{ {
categories: ['others'], categories: ['others'],
description: '', description: '',
input: { defaultValue: [],
defaultValue: [], inputType: SCSettingInputType.MultipleChoice,
inputType: 'multipleChoice',
values: [1, 2, 3, 4, 5, 6, 7, 8],
},
name: 'numbers', name: 'numbers',
order: 0, order: 0,
origin: { origin: {
@@ -393,6 +378,7 @@ describe('SettingsProvider', () => {
}, },
type: SCThingType.Setting, type: SCThingType.Setting,
uid: '', uid: '',
values: [1, 2, 3, 4, 5, 6, 7, 8],
}, },
]; ];
}); });
+91 -50
View File
@@ -16,25 +16,29 @@ import {Injectable} from '@angular/core';
import {Geolocation} from '@ionic-native/geolocation/ngx'; import {Geolocation} from '@ionic-native/geolocation/ngx';
import { import {
SCSetting, SCSetting,
SCSettingMultipleChoice,
SCSettingSingleChoice,
SCSettingValue, SCSettingValue,
SCSettingValues,
} from '@openstapps/core'; } from '@openstapps/core';
import {Logger} from '@openstapps/logger';
import * as deepMerge from 'deepmerge'; import * as deepMerge from 'deepmerge';
import {ConfigProvider} from '../config/config.provider'; import {ConfigProvider} from '../config/config.provider';
import {StorageProvider} from '../storage/storage.provider'; import {StorageProvider} from '../storage/storage.provider';
export const STORAGE_KEY_SETTINGS = 'settings'; export const STORAGE_KEY_SETTINGS = 'settings';
export const STORAGE_KEY_SETTINGS_SEPARATOR = '.'; export const STORAGE_KEY_SETTINGS_SEPARATOR = '.';
export const STORAGE_KEY_SETTING_VALUES = STORAGE_KEY_SETTINGS + STORAGE_KEY_SETTINGS_SEPARATOR + 'values'; export const STORAGE_KEY_SETTING_VALUES = `${STORAGE_KEY_SETTINGS}${STORAGE_KEY_SETTINGS_SEPARATOR}values`;
/** /**
* Category structure of settings cache * Category structure of settings cache
*/ */
export interface CategoryWithSettings { export interface CategoryWithSettings {
/**
* Category name
*/
category: string; category: string;
settings: { [key: string]: SCSetting }; /**
* Settings that belong in this category
*/
settings: { [key: string]: SCSetting; };
} }
/** /**
@@ -60,36 +64,61 @@ export interface SettingValueContainer {
/** /**
* Provider for app settings * Provider for app settings
*
*/ */
@Injectable() @Injectable()
export class SettingsProvider { export class SettingsProvider {
/**
* Order of the setting categories
*/
categoriesOrder: string[]; categoriesOrder: string[];
/**
* Is provider initialized
*/
initialized = false; initialized = false;
logger = new Logger(); /**
* Cache for the imported settings
*/
settingsCache: SettingsCache; settingsCache: SettingsCache;
/** /**
* Return true if all given values are valid to possible values in given settingInput * Return true if all given values are valid to possible values in given settingInput
* @param settingInput * @param possibleValues Possible values
* @param values * @param enteredValues Entered value
*/ */
public static checkMultipleChoiceValue(settingInput: SCSettingMultipleChoice, values: SCSettingValue[]): boolean { public static checkMultipleChoiceValue(
for (const value of values) { possibleValues: SCSettingValues | undefined,
if (!settingInput.values.includes(value)) { enteredValues: SCSettingValues,
): boolean {
if ( typeof possibleValues === 'undefined' ) {
return false;
}
for (const value of enteredValues) {
if (!possibleValues.includes(value)) {
return false; return false;
} }
} }
return true; return true;
} }
/** /**
* Returns true if given value is valid to possible values in given settingInput * Returns true if given value is valid to possible values in given settingInput
* @param settingInput * @param possibleValues Possible values
* @param value * @param enteredValue Entered value
*/ */
public static checkSingleChoiceValue(settingInput: SCSettingSingleChoice, value: SCSettingValue): boolean { public static checkSingleChoiceValue(
return settingInput.values !== undefined possibleValues: SCSettingValues | undefined,
&& settingInput.values.includes(value); enteredValue: SCSettingValue,
): boolean {
if ( typeof possibleValues === 'undefined' ) {
return false;
}
return possibleValues !== undefined
&& (Array.isArray(possibleValues)
&& possibleValues.includes(enteredValue));
} }
/** /**
@@ -97,19 +126,19 @@ export class SettingsProvider {
* @param setting setting to check value against * @param setting setting to check value against
* @param value value to validate * @param value value to validate
*/ */
public static validateValue(setting: SCSetting, value: any): boolean { public static validateValue(setting: SCSetting, value: SCSettingValue | SCSettingValues): boolean {
let isValueValid: boolean = false; let isValueValid = false;
switch (setting.input.inputType) { switch (setting.inputType) {
case 'number': case 'number':
if (typeof value === 'number') { if (typeof value === 'number') {
isValueValid = true; isValueValid = true;
} }
break; break;
case 'multipleChoice': case 'multiple choice':
if (!value.isArray) { if (!Array.isArray(value)) {
isValueValid = false; isValueValid = false;
} else { } else {
isValueValid = SettingsProvider.checkMultipleChoiceValue(setting.input, value); isValueValid = SettingsProvider.checkMultipleChoiceValue(setting.values, value);
} }
break; break;
case 'password': case 'password':
@@ -118,32 +147,43 @@ export class SettingsProvider {
isValueValid = true; isValueValid = true;
} }
break; break;
case 'singleChoice': case 'single choice':
isValueValid = SettingsProvider.checkSingleChoiceValue(setting.input, value); if (Array.isArray(value)) {
isValueValid = false;
} else {
isValueValid = SettingsProvider.checkSingleChoiceValue(setting.values, value);
}
break; break;
default: default:
} }
return isValueValid; return isValueValid;
} }
constructor(private storage: StorageProvider, /**
private configProvider: ConfigProvider, *
private geoLocation: Geolocation) { * @param storage TODO
* @param configProvider TODO
* @param geoLocation TODO
*/
constructor(private readonly storage: StorageProvider,
private readonly configProvider: ConfigProvider,
private readonly geoLocation: Geolocation) {
this.categoriesOrder = []; this.categoriesOrder = [];
this.settingsCache = {}; this.settingsCache = {};
} }
/** /**
* Add an Setting to the Cache if not exist and set undefined value to defaultValue * Add an Setting to the Cache if not exist and set undefined value to defaultValue
* @param setting * @param setting Setting with categories, defautlValue, name, input type and valid values
*/ */
private async addSetting(setting: SCSetting): Promise<void> { private async addSetting(setting: SCSetting): Promise<void> {
if (!this.categoryExists(setting.categories[0])) { if (!this.categoryExists(setting.categories[0])) {
await this.provideCategory(setting.categories[0]); await this.provideCategory(setting.categories[0]);
} }
if (!this.settingExists(setting.categories[0], setting.name)) { if (!this.settingExists(setting.categories[0], setting.name)) {
if (setting.input.value === undefined) { if (setting.value === undefined) {
setting.input.value = setting.input.defaultValue; setting.value = setting.defaultValue;
} }
this.settingsCache[setting.categories[0]].settings[setting.name] = setting; this.settingsCache[setting.categories[0]].settings[setting.name] = setting;
} }
@@ -162,9 +202,10 @@ export class SettingsProvider {
settingValuesContainer[categoryKey] = {}; settingValuesContainer[categoryKey] = {};
} }
settingValuesContainer[categoryKey][settingKey] = settingValuesContainer[categoryKey][settingKey] =
this.settingsCache[categoryKey].settings[settingKey].input.value; this.settingsCache[categoryKey].settings[settingKey].value;
} }
} }
return settingValuesContainer; return settingValuesContainer;
} }
@@ -196,11 +237,11 @@ export class SettingsProvider {
// if saved setting value exists set it, otherwise set to default value // if saved setting value exists set it, otherwise set to default value
if (typeof valuesContainer[categoryKey] !== 'undefined' if (typeof valuesContainer[categoryKey] !== 'undefined'
&& typeof valuesContainer[categoryKey][settingKey] !== 'undefined') { && typeof valuesContainer[categoryKey][settingKey] !== 'undefined') {
this.settingsCache[categoryKey].settings[settingKey].input.value = this.settingsCache[categoryKey].settings[settingKey].value =
valuesContainer[categoryKey][settingKey]; valuesContainer[categoryKey][settingKey];
} else { } else {
this.settingsCache[categoryKey].settings[settingKey].input.value = this.settingsCache[categoryKey].settings[settingKey].value =
this.settingsCache[categoryKey].settings[settingKey].input.defaultValue; this.settingsCache[categoryKey].settings[settingKey].defaultValue;
} }
} }
} }
@@ -228,7 +269,7 @@ export class SettingsProvider {
/** /**
* Returns true if category exists * Returns true if category exists
* @param category * @param category Category key name
*/ */
public categoryExists(category: string): boolean { public categoryExists(category: string): boolean {
return this.settingsCache[category] !== undefined; return this.settingsCache[category] !== undefined;
@@ -256,6 +297,7 @@ export class SettingsProvider {
} }
} }
} }
return true; return true;
} }
@@ -264,6 +306,7 @@ export class SettingsProvider {
*/ */
public async getCache(): Promise<SettingsCache> { public async getCache(): Promise<SettingsCache> {
await this.init(); await this.init();
return JSON.parse(JSON.stringify(this.settingsCache)); return JSON.parse(JSON.stringify(this.settingsCache));
} }
@@ -281,14 +324,13 @@ export class SettingsProvider {
* *
* @throws Exception if setting is not provided * @throws Exception if setting is not provided
*/ */
public async getSetting(category: string, name: string): Promise<any> { public async getSetting(category: string, name: string): Promise<SCSetting> {
await this.init(); await this.init();
if (this.settingExists(category, name)) { if (this.settingExists(category, name)) {
// return a copy of the settings // return a copy of the settings
return JSON.parse(JSON.stringify(this.settingsCache[category].settings[name])); return JSON.parse(JSON.stringify(this.settingsCache[category].settings[name]));
} else {
throw new Error(`Setting "${name}" not provided`);
} }
throw new Error(`Setting "${name}" not provided`);
} }
/** /**
@@ -302,10 +344,9 @@ export class SettingsProvider {
await this.init(); await this.init();
if (this.settingExists(category, name)) { if (this.settingExists(category, name)) {
// return a copy of the settings value // return a copy of the settings value
return JSON.parse(JSON.stringify(this.settingsCache[category].settings[name].input.value)); return JSON.parse(JSON.stringify(this.settingsCache[category].settings[name].value));
} else {
throw new Error(`Setting "${name}" not provided`);
} }
throw new Error(`Setting "${name}" not provided`);
} }
/** /**
@@ -340,7 +381,7 @@ export class SettingsProvider {
async resetDefault(): Promise<void> { async resetDefault(): Promise<void> {
for (const catKey of Object.keys(this.settingsCache)) { for (const catKey of Object.keys(this.settingsCache)) {
for (const settingKey of Object.keys(this.settingsCache[catKey].settings)) { for (const settingKey of Object.keys(this.settingsCache[catKey].settings)) {
const settingInput = this.settingsCache[catKey].settings[settingKey].input; const settingInput = this.settingsCache[catKey].settings[settingKey];
settingInput.value = settingInput.defaultValue; settingInput.value = settingInput.defaultValue;
} }
} }
@@ -373,24 +414,24 @@ export class SettingsProvider {
/** /**
* Sets a valid value of a setting and persists changes in storage * Sets a valid value of a setting and persists changes in storage
* @param category * @param category Category key name
* @param name * @param name Setting key name
* @param value * @param value Value to be set
* *
* @throws Exception if setting is not provided or value not valid to the settings inputType * @throws Exception if setting is not provided or value not valid to the settings inputType
*/ */
public async setSettingValue(category: string, name: string, public async setSettingValue(category: string, name: string,
value: any): Promise<void> { value: SCSettingValue | SCSettingValues): Promise<void> {
await this.init(); await this.init();
if (this.settingExists(category, name)) { if (this.settingExists(category, name)) {
const setting: SCSetting = this.settingsCache[category].settings[name]; const setting: SCSetting = this.settingsCache[category].settings[name];
const isValueValid = SettingsProvider.validateValue(setting, value); const isValueValid = SettingsProvider.validateValue(setting, value);
if (isValueValid) { if (isValueValid) {
this.settingsCache[category].settings[name].input.value = value; this.settingsCache[category].settings[name].value = value;
await this.saveSettingValues(); await this.saveSettingValues();
} else { } else {
throw new Error(`Value "${value}" of type throw new Error(`Value "${value}" of type
${typeof value} is not valid for ${setting.input.inputType}`); ${typeof value} is not valid for ${setting.inputType}`);
} }
} else { } else {
throw new Error(`setting ${name} is not provided`); throw new Error(`setting ${name} is not provided`);
@@ -399,8 +440,8 @@ export class SettingsProvider {
/** /**
* Returns true if setting in category exists * Returns true if setting in category exists
* @param category * @param category Category key name
* @param setting * @param setting Setting key name
*/ */
public settingExists(category: string, setting: string): boolean { public settingExists(category: string, setting: string): boolean {
return this.categoryExists(category) && this.settingsCache[category].settings[setting] !== undefined; return this.categoryExists(category) && this.settingsCache[category].settings[setting] !== undefined;
+10 -7
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018 StApps * Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -12,16 +12,19 @@
* 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 { NgModule } from '@angular/core'; import {NgModule} from '@angular/core';
import { IonicStorageModule } from '@ionic/storage'; import {IonicStorageModule} from '@ionic/storage';
import { StorageProvider } from './storage.provider'; import {StorageProvider} from './storage.provider';
/**
* Angular storage provider module
*/
@NgModule({ @NgModule({
imports: [ imports: [
IonicStorageModule.forRoot() IonicStorageModule.forRoot(),
], ],
providers: [ providers: [
StorageProvider StorageProvider,
] ],
}) })
export class StorageModule {} export class StorageModule {}
+97 -87
View File
@@ -20,24 +20,29 @@ import {Storage} from '@ionic/storage';
*/ */
@Injectable() @Injectable()
export class StorageProvider { export class StorageProvider {
constructor(private storage: Storage) {
}
/** /**
* Initializes the storage (waits until it's ready) *
* @param storage TODO
*/ */
async init(): Promise<any> { constructor(private readonly storage: Storage) {
return this.storage.ready();
} }
/** /**
* Puts a value of type T into the storage using provided key * Deletes storage entries using keys used to save them
* *
* @param key Unique indentifier * @param keys Unique identifiers of the resources for deletion
* @param value Resource to store under the key
*/ */
async put<T>(key: string, value: T): Promise<T> { async delete(...keys: string[]): Promise<void> {
return this.storage.set(key, value); keys.forEach(async (key) => {
await this.storage.remove(key);
});
}
/**
* Deletes all the entries in the storage (empty the storage)
*/
async deleteAll(): Promise<void> {
return this.storage.clear();
} }
/** /**
@@ -50,68 +55,23 @@ export class StorageProvider {
if (!entry) { if (!entry) {
throw new Error('Value not found.'); throw new Error('Value not found.');
} }
return entry; return entry;
} }
// tslint:disable:no-any
/** /**
* Gets values from the storage using the provided pattern * Retrieves all the storage entries
*
* @param pattern Regular expression or text to test existing storage keys with
*/ */
async search<T>(pattern: RegExp | string): Promise<Map<string, T>> { async getAll(): Promise<Map<string, any>> {
const map: Map<string, any> = new Map(); const map: Map<string, any> = new Map();
const check = (input: RegExp | string) => {
if (input instanceof RegExp) {
return (p: any, k: string): boolean => {
return p.test(k);
};
} else {
return (p: any, k: string): boolean => {
return k.includes(p);
};
}
};
const checkIt = check(pattern);
await this.storage.forEach((value, key) => { await this.storage.forEach((value, key) => {
if (checkIt(pattern, key)) { map.set(key, value);
map.set(key, value);
}
}); });
return map; return map;
} }
/**
* Deletes storage entries using keys used to save them
*
* @param keys Unique identifiers of the resources for deletion
*/
async delete(...keys: string[]): Promise<void> {
await keys.forEach((key) => {
this.storage.remove(key);
});
}
/**
* Deletes all the entries in the storage (empty the storage)
*/
async deleteAll(): Promise<void> {
return this.storage.clear();
}
/**
* Saves multiple entries into the storage
*
* @param entries Resources to be put into the storage
*/
async putMultiple(entries: Map<string, any>): Promise<Map<string, any>> {
const puts: Array<Promise<any>> = [];
entries.forEach(async (value, key) => {
puts.push(this.put(key, value));
});
await Promise.all(puts);
return entries;
}
/** /**
* Retrieves multiple entries from the storage using their keys * Retrieves multiple entries from the storage using their keys
* *
@@ -127,34 +87,10 @@ export class StorageProvider {
gets.push(getToMap(key)); gets.push(getToMap(key));
}); });
await Promise.all(gets); await Promise.all(gets);
return map; return map;
} }
/**
* Retrieves all the storage entries
*/
async getAll(): Promise<Map<string, any>> {
const map: Map<string, any> = new Map();
await this.storage.forEach((value, key) => {
map.set(key, value);
});
return map;
}
/**
* Provides a number of entries in the storage (number of keys)
*/
async length(): Promise<number> {
return this.storage.length();
}
/**
* Provides information if storage is empty or not
*/
async isEmpty(): Promise<boolean> {
return (await this.storage.length()) === 0;
}
/** /**
* Provides information if storage has an entry with the given key * Provides information if storage has an entry with the given key
* *
@@ -163,4 +99,78 @@ export class StorageProvider {
async has(key: string): Promise<boolean> { async has(key: string): Promise<boolean> {
return (await this.storage.keys()).includes(key); return (await this.storage.keys()).includes(key);
} }
/**
* Initializes the storage (waits until it's ready)
*/
async init(): Promise<any> {
return this.storage.ready();
}
/**
* Provides information if storage is empty or not
*/
async isEmpty(): Promise<boolean> {
return (await this.storage.length()) === 0;
}
/**
* Provides a number of entries in the storage (number of keys)
*/
async length(): Promise<number> {
return this.storage.length();
}
/**
* Puts a value of type T into the storage using provided key
*
* @param key Unique indentifier
* @param value Resource to store under the key
*/
async put<T>(key: string, value: T): Promise<T> {
return this.storage.set(key, value);
}
/**
* Saves multiple entries into the storage
*
* @param entries Resources to be put into the storage
*/
async putMultiple(entries: Map<string, any>): Promise<Map<string, any>> {
const puts: Array<Promise<any>> = [];
entries.forEach(async (value, key) => {
puts.push(this.put(key, value));
});
await Promise.all(puts);
return entries;
}
/**
* Gets values from the storage using the provided pattern
*
* @param pattern Regular expression or text to test existing storage keys with
*/
async search<T>(pattern: RegExp | string): Promise<Map<string, T>> {
const map: Map<string, any> = new Map();
const check = (input: RegExp | string) => {
if (input instanceof RegExp) {
return (p: any, k: string): boolean => {
return p.test(k);
};
}
return (p: any, k: string): boolean => {
return k.includes(p);
};
};
const checkIt = check(pattern);
await this.storage.forEach((value, key) => {
if (checkIt(pattern, key)) {
map.set(key, value);
}
});
return map;
}
} }
+9 -8
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018 StApps * Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -12,15 +12,16 @@
* 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 { enableProdMode } from '@angular/core'; import {enableProdMode} from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {Logger} from '@openstapps/logger';
import { AppModule } from './app/app.module'; import {AppModule} from './app/app.module';
import { environment } from './environments/environment'; import {environment} from './environments/environment';
if (environment.production) { if (environment.production) {
enableProdMode(); enableProdMode();
} }
platformBrowserDynamic().bootstrapModule(AppModule) platformBrowserDynamic()
.catch(err => console.log(err)); .bootstrapModule(AppModule)
.catch(async (err) => await Logger.error(err));
+8 -10
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018 StApps * Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -32,7 +32,9 @@
* BROWSER POLYFILLS * BROWSER POLYFILLS
*/ */
/** IE9, IE10 and IE11 requires all of the following polyfills. **/ /*
* IE9, IE10 and IE11 requires all of the following polyfills.
*/
// import 'core-js/es6/symbol'; // import 'core-js/es6/symbol';
// import 'core-js/es6/object'; // import 'core-js/es6/object';
// import 'core-js/es6/function'; // import 'core-js/es6/function';
@@ -54,27 +56,23 @@
/** IE10 and IE11 requires the following for the Reflect API. */ /** IE10 and IE11 requires the following for the Reflect API. */
// import 'core-js/es6/reflect'; // import 'core-js/es6/reflect';
/*
/** Evergreen browsers require these. **/ * Evergreen browsers require these.
*/
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
import 'core-js/es7/reflect'; import 'core-js/es7/reflect';
/** /**
* Required to support Web Animations `@angular/platform-browser/animations`. * Required to support Web Animations `@angular/platform-browser/animations`.
* Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
**/ */
// import 'web-animations-js'; // Run `npm install --save web-animations-js`. // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/*************************************************************************************************** /***************************************************************************************************
* Zone JS is required by Angular itself. * Zone JS is required by Angular itself.
*/ */
import 'zone.js/dist/zone'; // Included with Angular CLI. import 'zone.js/dist/zone'; // Included with Angular CLI.
/*************************************************************************************************** /***************************************************************************************************
* APPLICATION IMPORTS * APPLICATION IMPORTS
*/ */
+11 -8
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018 StApps * Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -14,21 +14,24 @@
*/ */
// This file is required by karma.conf.js and loads recursively all the .spec and framework files // This file is required by karma.conf.js and loads recursively all the .spec and framework files
import { getTestBed } from '@angular/core/testing'; import {getTestBed} from '@angular/core/testing';
import { import {
BrowserDynamicTestingModule, BrowserDynamicTestingModule,
platformBrowserDynamicTesting platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing'; } from '@angular/platform-browser-dynamic/testing';
import 'zone.js/dist/zone-testing'; import 'zone.js/dist/zone-testing';
// tslint:disable-next-line:no-any
declare const require: any; declare const require: any;
// First, initialize the Angular testing environment. // First, initialize the Angular testing environment.
getTestBed().initTestEnvironment( getTestBed()
BrowserDynamicTestingModule, .initTestEnvironment(
platformBrowserDynamicTesting() BrowserDynamicTestingModule,
); platformBrowserDynamicTesting(),
);
// Then we find all the tests. // Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/); const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules. // And load the modules.
context.keys().map(context); context.keys()
.map(context);