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

View File

@@ -17,6 +17,9 @@ import {DataModule} from '../data/data.module';
import {StorageModule} from '../storage/storage.module';
import {ConfigProvider} from './config.provider';
/**
* TODO
*/
@NgModule({
imports: [
StorageModule,

View File

@@ -13,7 +13,7 @@
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
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 {StorageProvider} from '../storage/storage.provider';
import {ConfigProvider, STORAGE_KEY_CONFIG} from './config.provider';
@@ -207,10 +207,8 @@ const sampleIndexResponse: SCIndexResponse = {
settings: [
{
categories: ['credentials'],
input: {
defaultValue: '',
inputType: 'text',
},
defaultValue: '',
inputType: SCSettingInputType.Text,
name: 'username',
order: 0,
origin: {

View File

@@ -39,12 +39,25 @@ export const STORAGE_KEY_CONFIG = 'stapps.config';
*/
@Injectable()
export class ConfigProvider {
/**
* TODO
*/
client: Client;
/**
* TODO
*/
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);
}
@@ -77,9 +90,8 @@ export class ConfigProvider {
}
if (typeof this.config.app[attribute] !== 'undefined') {
return this.config.app[attribute];
} else {
throw new ConfigValueNotAvailable(attribute);
}
throw new ConfigValueNotAvailable(attribute);
}
/**
@@ -96,10 +108,10 @@ export class ConfigProvider {
try {
this.config = await this.loadLocal();
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) {
loadError = new WrongConfigVersionInStorage(environment.backend_version, this.config.backend.SCVersion);
this.logger.warn(loadError);
Logger.warn(loadError);
}
} catch (error) {
loadError = error;
@@ -109,16 +121,18 @@ export class ConfigProvider {
const fetchedConfig: SCIndexResponse = await this.fetch();
await this.set(fetchedConfig);
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) {
fetchError = error;
}
// check for occurred errors and throw them
if (typeof loadError !== 'undefined' && typeof fetchError !== 'undefined') {
throw new ConfigInitError();
} else if (typeof loadError !== 'undefined') {
}
if (typeof loadError !== 'undefined') {
throw loadError;
} else if (typeof fetchError !== 'undefined') {
}
if (typeof fetchError !== 'undefined') {
throw fetchError;
}
}
@@ -132,7 +146,7 @@ export class ConfigProvider {
await this.storageProvider.init();
// get local configuration
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();
}

View File

@@ -13,8 +13,11 @@
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Injectable} from '@angular/core';
import {SCBackendAggregationConfiguration, SCFacet, SCThing, SCFacetBucket} from '@openstapps/core';
import {SCBackendAggregationConfiguration, SCFacet, SCFacetBucket, SCThing} from '@openstapps/core';
/**
* TODO
*/
@Injectable()
export class DataFacetsProvider {
// tslint:disable-next-line:no-empty
@@ -29,7 +32,8 @@ export class DataFacetsProvider {
* @param bucketsMap Buckets array transformed into a 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) => {
if (typeof bucketsMap[field] !== 'undefined') {
bucketsMap[field] = bucketsMap[field] + 1;
@@ -37,6 +41,7 @@ export class DataFacetsProvider {
bucketsMap[field] = 1;
}
});
return bucketsMap;
}
@@ -45,61 +50,16 @@ export class DataFacetsProvider {
*
* @param buckets Buckets from a facet
*/
bucketsToMap(buckets: SCFacetBucket[]): {[key: string]: number} {
const bucketsMap: {[key: string]: number} = {};
// tslint:disable-next-line:prefer-function-over-method
bucketsToMap(buckets: SCFacetBucket[]): {[key: string]: number; } {
const bucketsMap: {[key: string]: number; } = {};
buckets.forEach((bucket) => {
bucketsMap[bucket.key] = bucket.count;
});
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
*
@@ -114,12 +74,12 @@ export class DataFacetsProvider {
if (items.length === 0) {
if (facets.length === 0) {
return [];
} else {
return facets;
}
return 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) => {
aggregations.forEach((aggregation) => {
let fieldValues: string | string[] = item[aggregation.fieldName];
@@ -142,6 +102,58 @@ export class DataFacetsProvider {
}
});
});
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;
}
}

View File

@@ -22,6 +22,9 @@ const dataRoutes: Routes = [
{path: 'data-detail/:uid', component: DataDetailComponent},
];
/**
* TODO
*/
@NgModule({
exports: [
RouterModule,

View File

@@ -26,13 +26,15 @@ import {DataRoutingModule} from './data-routing.module';
import {DataProvider} from './data.provider';
import {DataDetailContentComponent} from './detail/data-detail-content.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 {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 {OriginInListComponent} from './elements/origin-in-list.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 {DataListComponent} from './list/data-list.component';
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 {VideoDetailContentComponent} from './types/video/video-detail-content.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({
declarations: [
OffersDetailComponent,

View File

@@ -24,41 +24,64 @@ export enum DataScope {
Remote = 'remote',
}
/*
Generated class for the DataProvider provider.
See https://angular.io/guide/dependency-injection for more info on providers
and Angular DI.
*/
/**
* Generated class for the DataProvider provider.
*
* See https://angular.io/guide/dependency-injection for more info on providers
* and Angular DI.
*/
@Injectable()
export class DataProvider {
private _storagePrefix: string = 'stapps.data';
// @TODO: get backendUrl and appVersion and storagePrefix from config (module)
appVersion: string = '1.0.6';
backendUrl: string = 'http://localhost:3000';
/**
* TODO
*/
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;
/**
* TODO
*/
storageProvider: StorageProvider;
/**
* TODO
*
* @param stAppsWebHttpClient TODO
* @param storageProvider TODO
*/
constructor(stAppsWebHttpClient: StAppsWebHttpClient, storageProvider: StorageProvider) {
this.client = new Client(stAppsWebHttpClient, this.backendUrl, this.appVersion);
this.storageProvider = storageProvider;
}
get storagePrefix(): string {
return this._storagePrefix;
}
set storagePrefix(storagePrefix) {
this._storagePrefix = storagePrefix;
/**
* 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));
}
/**
* Provides key for storing data into the local database
*
* @param uid Unique identifier of a resource
* Delete all the previously saved data items
*/
getDataKey(uid: string): string {
return `${this.storagePrefix}.${uid}`;
async deleteAll(): Promise<void> {
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();
map.set(DataScope.Local, await this.get(uid, DataScope.Local));
map.set(DataScope.Remote, await this.get(uid, DataScope.Remote));
return map;
}
@@ -101,6 +125,24 @@ export class DataProvider {
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
*
@@ -118,25 +160,9 @@ export class DataProvider {
type: (typeof type === 'undefined') ? item.type : type,
uid: item.uid,
};
// @TODO: Implementation for saving item into the backend (user's account)
return (await 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);
return ( this.storageProvider.put<SCSaveableThing<SCThings>>(this.getDataKey(item.uid), saveableItem));
}
/**
@@ -145,15 +171,20 @@ export class DataProvider {
* @param query - query to send to the backend
*/
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
*
* @param uid Unique identifier of the saved data item
* TODO
*/
async isSaved(uid: string): Promise<boolean> {
return this.storageProvider.has(this.getDataKey(uid));
get storagePrefix(): string {
return this._storagePrefix;
}
/**
* TODO
*/
set storagePrefix(storagePrefix) {
this._storagePrefix = storagePrefix;
}
}

View File

@@ -15,10 +15,16 @@
import {Component, Input} from '@angular/core';
import {SCThings} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-data-detail-content',
templateUrl: 'data-detail-content.html',
})
export class DataDetailContentComponent {
/**
* TODO
*/
@Input() item: SCThings;
}

View File

@@ -19,17 +19,35 @@ import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
import {SCLanguageCode, SCThing, SCUuid} from '@openstapps/core';
import {DataProvider, DataScope} from '../data.provider';
/**
* TODO
*/
@Component({
selector: 'stapps-data-detail',
styleUrls: ['data-detail.scss'],
templateUrl: 'data-detail.html',
})
export class DataDetailComponent {
/**
* TODO
*/
dataProvider: DataProvider;
/**
* TODO
*/
item: SCThing;
/**
* TODO
*/
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.language = translateService.currentLang as SCLanguageCode;
translateService.onLangChange.subscribe((event: LangChangeEvent) => {
@@ -43,11 +61,16 @@ export class DataDetailComponent {
* @param uid Unique identifier of a thing
*/
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;
});
});
}
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() {
this.getItem(this.route.snapshot.paramMap.get('uid') || '');
}

View File

@@ -15,10 +15,16 @@
import {Component, Input} from '@angular/core';
import {SCPostalAddress} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-address-detail',
templateUrl: 'address-detail.html',
})
export class AddressDetailComponent {
/**
* TODO
*/
@Input() address: SCPostalAddress;
}

View File

@@ -14,11 +14,20 @@
*/
import {Component, Input} from '@angular/core';
/**
* TODO
*/
@Component({
selector: 'stapps-long-inline-text',
templateUrl: 'long-inline-text.html',
})
export class LongInlineText {
@Input() text: string;
/**
* TODO
*/
@Input() size: number;
/**
* TODO
*/
@Input() text: string;
}

View File

@@ -15,11 +15,20 @@
import {Component, Input} from '@angular/core';
import {SCAcademicPriceGroup, SCThingThatCanBeOfferedOffer} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-offers-detail',
templateUrl: 'offers-detail.html',
})
export class OffersDetailComponent {
/**
* TODO
*/
objectKeys = Object.keys;
/**
* TODO
*/
@Input() offers: Array<SCThingThatCanBeOfferedOffer<SCAcademicPriceGroup>>;
}

View File

@@ -15,10 +15,16 @@
import {Component, Input} from '@angular/core';
import {SCAcademicPriceGroup, SCThingThatCanBeOfferedOffer} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-offers-in-list',
templateUrl: 'offers-in-list.html',
})
export class OffersInListComponent {
/**
* TODO
*/
@Input() offers: Array<SCThingThatCanBeOfferedOffer<SCAcademicPriceGroup>>;
}

View File

@@ -15,10 +15,16 @@
import {Component, Input} from '@angular/core';
import {SCThingOrigin} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-origin-detail',
templateUrl: 'origin-detail.html',
})
export class OriginDetailComponent {
/**
* TODO
*/
@Input() origin: SCThingOrigin;
}

View File

@@ -15,10 +15,16 @@
import {Component, Input} from '@angular/core';
import {SCThingOrigin} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-origin-in-list',
templateUrl: 'origin-in-list.html',
})
export class OriginInListComponent {
/**
* TODO
*/
@Input() origin: SCThingOrigin;
}

View File

@@ -15,18 +15,41 @@
import {Component, Input} from '@angular/core';
import {isThing, SCThing} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-simple-card',
templateUrl: 'simple-card.html',
})
export class SimpleCardComponent {
areThings: boolean = false;
/**
* TODO
*/
areThings = false;
/**
* TODO
*/
@Input() content: string | string[] | SCThing[];
@Input() isMarkdown: boolean = false;
/**
* TODO
*/
@Input() isMarkdown = false;
/**
* TODO
*/
@Input() title: string;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
isString(data: any): data is string {
return typeof data === 'string';
}
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
isThing(something: any): something is SCThing {
return isThing(something);
}

View File

@@ -14,6 +14,9 @@
*/
import {Component} from '@angular/core';
/**
* TODO
*/
@Component({
selector: 'stapps-skeleton-list-item',
templateUrl: 'skeleton-list-item.html',

View File

@@ -14,6 +14,9 @@
*/
import {Component} from '@angular/core';
/**
* TODO
*/
@Component({
selector: 'stapps-skeleton-simple-card',
templateUrl: 'skeleton-simple-card.html',

View File

@@ -15,19 +15,32 @@
import {Component, Input, OnInit} from '@angular/core';
import {SCThings} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-data-list-item',
styleUrls: ['data-list-item.scss'],
templateUrl: 'data-list-item.html',
})
export class DataListItem implements OnInit {
/**
* TODO
*/
@Input() item: SCThings;
/**
* TODO
*/
constructor() {
// noop
// this.item is not available yet
}
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() {
// noop
// this.item is available now - the template is loaded and compiled

View File

@@ -19,24 +19,58 @@ import {Subject} from 'rxjs';
import {debounceTime, distinctUntilChanged} from 'rxjs/operators';
import {DataProvider} from '../data.provider';
/**
* TODO
*/
@Component({
selector: 'stapps-data-list',
templateUrl: 'data-list.html',
})
export class DataListComponent {
/**
* TODO
*/
dataProvider: DataProvider;
/**
* TODO
*/
from = 0;
/**
* TODO
*/
items: SCThing[];
selectedItem: any;
loaded: boolean = false;
size: number = 30;
from: number = 0;
/**
* TODO
*/
loaded = false;
/**
* TODO
*/
query: string;
/**
* TODO
*/
queryChanged: Subject<string> = new Subject<string>();
/**
* TODO
*/
selectedItem: any;
/**
* TODO
*/
size = 30;
/**
*
* @param alertController TODO
* @param dataProvider TODO
*/
constructor(
private alertController: AlertController,
private readonly alertController: AlertController,
dataProvider: DataProvider,
) {
this.dataProvider = dataProvider;
@@ -52,31 +86,41 @@ export class DataListComponent {
this.fetchItems();
}
/**
* TODO
*/
private async fetchItems(): Promise<any> {
return this.dataProvider.search({
from: this.from,
query: this.query,
size: this.size,
} as any).then((result) => {
this.items = result.data;
this.loaded = true;
}, async (err) => {
const alert: HTMLIonAlertElement = await this.alertController.create({
buttons: ['Dismiss'],
header: 'Error',
subHeader: err.message,
});
} as any)
.then((result) => {
this.items = result.data;
this.loaded = true;
}, async (err) => {
const alert: HTMLIonAlertElement = await this.alertController.create({
buttons: ['Dismiss'],
header: 'Error',
subHeader: err.message,
});
await alert.present();
});
await alert.present();
});
}
/**
* TODO
*/
async loadMore(event: any): Promise<void> {
this.from += this.size;
await this.fetchItems();
event.target.complete();
}
/**
* TODO
*/
search(query: string) {
this.queryChanged.next(query);
}

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
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -14,15 +14,18 @@
*/
import {HttpClient, HttpResponse} from '@angular/common/http';
import {Injectable} from '@angular/core';
import {HttpClientInterface} from '@openstapps/api/lib/httpClientInterface';
import {HttpClientRequest} from '@openstapps/api/lib/httpClientInterface';
import {HttpClientInterface, HttpClientRequest} from '@openstapps/api/lib/http-client-interface';
/**
* HttpClient that is based on angular's HttpClient (@TODO: move it to provider or independent package)
*/
@Injectable()
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,
): Promise<Response<TYPE_OF_BODY>> {
const options: {
/**
* TODO
*/
[key: string]: any;
/**
* TODO
*/
observe: 'response';
} = {
body: {},
@@ -53,6 +62,7 @@ export class StAppsWebHttpClient implements HttpClientInterface {
const response: HttpResponse<TYPE_OF_BODY> = await this.http.request<TYPE_OF_BODY>(
requestConfig.method || 'GET', requestConfig.url.toString(), options)
.toPromise();
return Object.assign(response, {statusCode: response.status, body: response.body || {}});
} catch (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
*/
export interface Response<TYPE_OF_BODY> extends HttpResponse<TYPE_OF_BODY> {
/**
* TODO
*/
body: TYPE_OF_BODY;
/**
* TODO
*/
statusCode: number;
}

View File

@@ -16,16 +16,34 @@ import {Component, Input} from '@angular/core';
import {SCArticle, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-article-detail-content',
templateUrl: 'article-detail-content.html',
})
export class ArticleDetailContentComponent {
/**
* TODO
*/
@Input() item: SCArticle;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator;
/**
* TODO
*/
constructor() {
this.translator = new SCThingTranslator(this.language, 'de');
this.translator = new SCThingTranslator(this.language);
}
}

View File

@@ -16,14 +16,24 @@ import {Component, Input} from '@angular/core';
import {SCArticle} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({
selector: 'stapps-article-list-item',
templateUrl: 'article-list-item.html',
})
export class ArticleListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCArticle;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() {
// TODO: translation
}

View File

@@ -16,16 +16,34 @@ import {Component, Input} from '@angular/core';
import {SCCatalog, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-catalog-detail-content',
templateUrl: 'catalog-detail-content.html',
})
export class CatalogDetailContentComponent {
/**
* TODO
*/
@Input() item: SCCatalog;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator;
/**
* TODO
*/
constructor() {
this.translator = new SCThingTranslator(this.language, 'de');
this.translator = new SCThingTranslator(this.language);
}
}

View File

@@ -16,14 +16,24 @@ import {Component, Input} from '@angular/core';
import {SCCatalog} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({
selector: 'stapps-catalog-list-item',
templateUrl: 'catalog-list-item.html',
})
export class CatalogListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCCatalog;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() {
// TODO: translation
}

View File

@@ -16,16 +16,34 @@ import {Component, Input} from '@angular/core';
import {SCDateSeries, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-date-series-detail-content',
templateUrl: 'date-series-detail-content.html',
})
export class DateSeriesDetailContentComponent {
/**
* TODO
*/
@Input() item: SCDateSeries;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator;
/**
* TODO
*/
constructor() {
this.translator = new SCThingTranslator(this.language, 'de');
this.translator = new SCThingTranslator(this.language);
}
}

View File

@@ -16,14 +16,24 @@ import {Component, Input} from '@angular/core';
import {SCDateSeries} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({
selector: 'stapps-date-series-list-item',
templateUrl: 'date-series-list-item.html',
})
export class DateSeriesListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCDateSeries;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() {
// TODO: translation
}

View File

@@ -16,16 +16,34 @@ import {Component, Input} from '@angular/core';
import {SCDish, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-dish-detail-content',
templateUrl: 'dish-detail-content.html',
})
export class DishDetailContentComponent {
/**
* TODO
*/
@Input() item: SCDish;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator;
/**
* TODO
*/
constructor() {
this.translator = new SCThingTranslator(this.language, 'de');
this.translator = new SCThingTranslator(this.language);
}
}

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
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -17,12 +17,18 @@ import {SCDish} from '@openstapps/core';
// import {SettingsProvider} from '../../../settings/settings.provider';
import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({
selector: 'stapps-dish-list-item',
templateUrl: 'dish-list-item.html',
})
export class DishListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCDish;
// settingsProvider: SettingsProvider;
@@ -31,6 +37,10 @@ export class DishListItem extends DataListItem {
// this.settingsProvider = settingsProvider;
// }
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() {
// TODO: translation...
}

View File

@@ -16,16 +16,34 @@ import {Component, Input} from '@angular/core';
import {SCAcademicEvent, SCSportCourse, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-event-detail-content',
templateUrl: 'event-detail-content.html',
})
export class EventDetailContentComponent {
/**
* TODO
*/
@Input() item: SCAcademicEvent | SCSportCourse;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator;
/**
* TODO
*/
constructor() {
this.translator = new SCThingTranslator(this.language, 'de');
this.translator = new SCThingTranslator(this.language);
}
}

View File

@@ -16,13 +16,23 @@ import {Component, Input} from '@angular/core';
import {SCAcademicEvent, SCSportCourse} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({
selector: 'stapps-event-list-item',
templateUrl: 'event-list-item.html',
})
export class EventListItemComponent extends DataListItem {
/**
* TODO
*/
@Input() item: SCAcademicEvent | SCSportCourse;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() {
// TODO: translation
}

View File

@@ -16,16 +16,34 @@ import {Component, Input} from '@angular/core';
import {SCFavorite, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-favorite-detail-content',
templateUrl: 'favorite-detail-content.html',
})
export class FavoriteDetailContentComponent {
/**
* TODO
*/
@Input() item: SCFavorite;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator;
/**
* TODO
*/
constructor() {
this.translator = new SCThingTranslator(this.language, 'de');
this.translator = new SCThingTranslator(this.language);
}
}

View File

@@ -16,14 +16,24 @@ import {Component, Input} from '@angular/core';
import {SCFavorite} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({
selector: 'stapps-favorite-list-item',
templateUrl: 'favorite-list-item.html',
})
export class FavoriteListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCFavorite;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() {
// TODO: translation
}

View File

@@ -16,16 +16,34 @@ import {Component, Input} from '@angular/core';
import {SCMessage, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-message-detail-content',
templateUrl: 'message-detail-content.html',
})
export class MessageDetailContentComponent {
/**
* TODO
*/
@Input() item: SCMessage;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator;
/**
* TODO
*/
constructor() {
this.translator = new SCThingTranslator(this.language, 'de');
this.translator = new SCThingTranslator(this.language);
}
}

View File

@@ -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 *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>

View File

@@ -16,14 +16,24 @@ import {Component, Input} from '@angular/core';
import {SCMessage} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({
selector: 'stapps-message-list-item',
templateUrl: 'message-list-item.html',
})
export class MessageListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCMessage;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() {
// TODO: translation
}

View File

@@ -2,8 +2,8 @@
<ion-row>
<ion-col>
<h2 class="name">{{item.name}}</h2>
<p *ngIf="item.message">
<stapps-long-inline-text [text]="item.message" [size]="80"></stapps-long-inline-text>
<p *ngIf="item.messageBody">
<stapps-long-inline-text [text]="item.messageBody" [size]="80"></stapps-long-inline-text>
</p>
<ion-note>{{item.type}}</ion-note>
</ion-col>

View File

@@ -16,16 +16,35 @@ import {Component, Input} from '@angular/core';
import {SCOrganization, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-organization-detail-content',
templateUrl: 'organization-detail-content.html',
})
export class OrganizationDetailContentComponent {
/**
* TODO
*/
@Input() item: SCOrganization;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator;
/**
* TODO
*/
constructor() {
this.translator = new SCThingTranslator(this.language, 'de');
this.translator = new SCThingTranslator(this.language);
}
}

View File

@@ -16,14 +16,24 @@ import {Component, Input} from '@angular/core';
import {SCOrganization} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({
selector: 'stapps-organization-list-item',
templateUrl: 'organization-list-item.html',
})
export class OrganizationListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCOrganization;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() {
// TODO: translation
}

View File

@@ -16,16 +16,35 @@ import {Component, Input} from '@angular/core';
import {SCPerson, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-person-detail-content',
templateUrl: 'person-detail-content.html',
})
export class PersonDetailContentComponent {
/**
* TODO
*/
@Input() item: SCPerson;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator;
/**
* TODO
*/
constructor() {
this.translator = new SCThingTranslator(this.language, 'de');
this.translator = new SCThingTranslator(this.language);
}
}

View File

@@ -16,14 +16,24 @@ import {Component, Input} from '@angular/core';
import {SCPerson} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({
selector: 'stapps-person-list-item',
templateUrl: 'person-list-item.html',
})
export class PersonListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCPerson;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() {
// TODO: translation
}

View File

@@ -16,16 +16,35 @@ import {Component, Input} from '@angular/core';
import {SCBuilding, SCFloor, SCPointOfInterest, SCRoom, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-place-detail-content',
templateUrl: 'place-detail-content.html',
})
export class PlaceDetailContentComponent {
/**
* TODO
*/
@Input() item: SCBuilding | SCRoom | SCPointOfInterest | SCFloor;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator;
/**
* TODO
*/
constructor() {
this.translator = new SCThingTranslator(this.language, 'de');
this.translator = new SCThingTranslator(this.language);
}
}

View File

@@ -16,14 +16,24 @@ import {Component, Input} from '@angular/core';
import {SCBuilding, SCFloor, SCPointOfInterest, SCRoom} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({
selector: 'stapps-place-list-item',
templateUrl: 'place-list-item.html',
})
export class PlaceListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCBuilding | SCRoom | SCPointOfInterest | SCFloor;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() {
// TODO: translation
}

View File

@@ -16,16 +16,35 @@ import {Component, Input} from '@angular/core';
import {SCSemester, SCThing, SCTranslations} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-semester-detail-content',
templateUrl: 'semester-detail-content.html',
})
export class SemesterDetailContentComponent {
/**
* TODO
*/
@Input() item: SCSemester;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator;
/**
* TODO
*/
constructor() {
this.translator = new SCThingTranslator(this.language, 'de');
this.translator = new SCThingTranslator(this.language);
}
}

View File

@@ -16,14 +16,23 @@ import {Component, Input} from '@angular/core';
import {SCSemester} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({
selector: 'stapps-semester-list-item',
templateUrl: 'semester-list-item.html',
})
export class SemesterListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCSemester;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() {
// TODO: translation
}

View File

@@ -16,16 +16,35 @@ import {Component, Input} from '@angular/core';
import {SCThing, SCTranslations, SCVideo} from '@openstapps/core';
import {SCThingTranslator} from '@openstapps/core';
/**
* TODO
*/
@Component({
selector: 'stapps-video-detail-content',
templateUrl: 'video-detail-content.html',
})
export class VideoDetailContentComponent {
/**
* TODO
*/
@Input() item: SCVideo;
/**
* TODO
*/
@Input() language: keyof SCTranslations<SCThing>;
/**
* TODO
*/
objectKeys = Object.keys;
/**
* TODO
*/
translator: SCThingTranslator;
/**
* TODO
*/
constructor() {
this.translator = new SCThingTranslator(this.language, 'de');
this.translator = new SCThingTranslator(this.language);
}
}

View File

@@ -16,14 +16,23 @@ import {Component, Input} from '@angular/core';
import {SCVideo} from '@openstapps/core';
import {DataListItem} from '../../list/data-list-item.component';
/**
* TODO
*/
@Component({
selector: 'stapps-video-list-item',
templateUrl: 'video-list-item.html',
})
export class VideoListItem extends DataListItem {
/**
* TODO
*/
@Input() item: SCVideo;
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ngOnInit() {
// TODO: translation
}

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
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -18,20 +18,23 @@ import {RouterModule} from '@angular/router';
import {IonicModule} from '@ionic/angular';
import {NavigationComponent} from './navigation/navigation.component';
/**
* TODO
*/
@NgModule({
declarations: [
NavigationComponent,
],
entryComponents: [
NavigationComponent
NavigationComponent,
],
exports: [
NavigationComponent
NavigationComponent,
],
imports: [
IonicModule.forRoot(),
CommonModule,
RouterModule
]
RouterModule,
],
})
export class MenuModule {}

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
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -26,6 +26,9 @@ import {Component} from '@angular/core';
templateUrl: 'navigation.html',
})
export class NavigationComponent {
/**
* TODO
*/
public pages = [
{title: 'Search', url: '/search', icon: 'search'},
{title: 'Settings', url: '/settings', icon: 'settings'},
@@ -35,6 +38,10 @@ export class NavigationComponent {
// Nothing yet.
}
/**
* TODO
*/
// tslint:disable-next-line:prefer-function-over-method
ionViewDidLoad() {
// console.log('ionViewDidLoad MenuPage');
}

View File

@@ -17,34 +17,56 @@ import {AlertController} from '@ionic/angular';
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
import {
SCSetting,
SCSettingValue,
SCSettingValues,
SCThingTranslator,
SCTranslations,
} from '@openstapps/core';
import {Logger} from '@openstapps/logger';
import {SettingsProvider} from '../settings.provider';
/**
* TODO
*/
@Component({
selector: 'stapps-settings-item',
templateUrl: 'settings-item.html',
})
export class SettingsItemComponent {
/**
* TODO
*/
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>;
logger = new Logger();
/**
* TODO
*/
@Input() setting: SCSetting;
/**
* TODO
*/
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.translator = new SCThingTranslator(this.language, 'de');
this.translator = new SCThingTranslator(this.language);
translateService.onLangChange.subscribe((event: LangChangeEvent) => {
this.isVisible = false;
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
setTimeout(() => this.isVisible = true);
});
@@ -58,7 +80,7 @@ export class SettingsItemComponent {
const permissionGranted = await this.settingsProvider.checkGeoLocationPermission();
if (!permissionGranted) {
// revert setting value
this.setting.input.value = false;
this.setting.value = false;
await this.presentGeoLocationAlert();
}
}
@@ -67,8 +89,10 @@ export class SettingsItemComponent {
* Shows alert with error message on denied user permission or disabled location services
*/
private async presentGeoLocationAlert() {
const title = await this.translateService.get('settings.geoLocation.permission_denied_title').toPromise();
const message = await this.translateService.get('settings.geoLocation.permission_denied_message').toPromise();
const title = await this.translateService.get('settings.geoLocation.permission_denied_title')
.toPromise();
const message = await this.translateService.get('settings.geoLocation.permission_denied_message')
.toPromise();
await this.presentAlert(title, message);
}
@@ -91,29 +115,34 @@ export class SettingsItemComponent {
* Handles value changes of the setting
*/
async settingChanged(): Promise<void> {
if (typeof this.setting.input.value !== 'undefined'
&& SettingsProvider.validateValue(this.setting, this.setting.input.value)) {
if (typeof this.setting.value !== 'undefined'
&& SettingsProvider.validateValue(this.setting, this.setting.value)) {
// handle general settings, with special actions
switch (this.setting.name) {
case 'language':
this.translateService.use(this.setting.input.value.toString());
this.translateService.use(this.setting.value.toString());
break;
case 'geoLocation':
if (this.setting.input.value) {
if (this.setting.value) {
await this.checkGeoLocationPermission();
}
break;
default:
}
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 {
// reset setting
this.setting.input.value =
await this.settingsProvider.getValue(this.setting.categories[0], this.setting.name);
this.setting.value =
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) {
return typeof (val);
}

View File

@@ -5,27 +5,27 @@
<ion-card-content>
<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-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 *ngSwitchCase="'text'">
<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 *ngSwitchCase="'password'">
<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 *ngSwitchCase="'singleChoice'">
<ion-label></ion-label>
<ion-toggle *ngIf="typeOf(setting.input.defaultValue) === 'boolean'" [(ngModel)]="setting.input.value" (ionChange)="settingChanged()"></ion-toggle>
<ion-select *ngIf="typeOf(setting.input.defaultValue) !== 'boolean'" interface="popover" [(ngModel)]="setting.input.value" (ionChange)="settingChanged()">
<ion-select-option *ngFor="let val of setting.input.values" [value]="val">
<ion-toggle *ngIf="typeOf(setting.defaultValue) === 'boolean'" [(ngModel)]="setting.value" (ionChange)="settingChanged()"></ion-toggle>
<ion-select *ngIf="typeOf(setting.defaultValue) !== 'boolean'" interface="popover" [(ngModel)]="setting.value" (ionChange)="settingChanged()">
<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>
</ion-select-option>
@@ -34,8 +34,8 @@
<ion-item *ngSwitchCase="'multipleChoice'">
<ion-label></ion-label>
<ion-select [(ngModel)]="setting.input.value" multiple="true" (ionChange)="settingChanged()">
<ion-select-option *ngFor="let val of setting.input.values" [value]="val">
<ion-select [(ngModel)]="setting.value" multiple="true" (ionChange)="settingChanged()">
<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>
</ion-select-option>

View File

@@ -18,29 +18,58 @@ import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
import {SCSettingMeta, SCThingTranslator, SCTranslations} from '@openstapps/core';
import {SettingsCache, SettingsProvider} from '../settings.provider';
/**
* TODO
*/
@Component({
selector: 'stapps-settings-page',
templateUrl: 'settings-page.html',
})
export class SettingsPageComponent {
/**
* Order of the categories
*/
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>;
/**
* Meta information about settings
*/
meta = SCSettingMeta;
/**
* TODO
*/
objectKeys = Object.keys;
/**
* TODO
*/
settingsCache: SettingsCache;
/**
* TODO
*/
translator: SCThingTranslator;
constructor(private alertController: AlertController,
private settingsProvider: SettingsProvider,
private toastController: ToastController,
private translateService: TranslateService) {
/**
*
* @param alertController TODO
* @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.translator = new SCThingTranslator(this.language, 'de');
this.translator = new SCThingTranslator(this.language);
translateService.onLangChange.subscribe((event: LangChangeEvent) => {
this.language = event.lang as keyof SCTranslations<any>;
this.translator = new SCThingTranslator(this.language, 'de');
this.translator = new SCThingTranslator(this.language);
});
this.settingsCache = {};
this.categoriesOrder = settingsProvider.getCategoriesOrder();
@@ -66,6 +95,9 @@ export class SettingsPageComponent {
this.settingsCache = await this.settingsProvider.getCache();
}
/**
* Component initialize method
*/
async ngOnInit() {
await this.loadSettings();
}
@@ -74,10 +106,14 @@ export class SettingsPageComponent {
* Presents an alert to the user to reset settings to default values
*/
async presentResetAlert() {
const cancelText = await this.translateService.get('settings.resetAlert.buttonCancel').toPromise();
const yesText = await this.translateService.get('settings.resetAlert.buttonYes').toPromise();
const title = await this.translateService.get('settings.resetAlert.title').toPromise();
const message = await this.translateService.get('settings.resetAlert.message').toPromise();
const cancelText = await this.translateService.get('settings.resetAlert.buttonCancel')
.toPromise();
const yesText = await this.translateService.get('settings.resetAlert.buttonYes')
.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({
buttons: [

View File

@@ -12,7 +12,7 @@
<ion-list *ngFor="let categoryKey of categoriesOrder ">
<div *ngIf="objectKeys(settingsCache).includes(categoryKey)">
<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>
</ion-item-divider>
<stapps-settings-item *ngFor="let settingKeys of objectKeys(settingsCache[categoryKey].settings)" [setting]="settingsCache[categoryKey].settings[settingKeys]"></stapps-settings-item>

View File

@@ -29,6 +29,9 @@ const settingsRoutes: Routes = [
{path: 'settings', component: SettingsPageComponent},
];
/**
* Settings Module
*/
@NgModule({
declarations: [
SettingsPageComponent,

View File

@@ -13,7 +13,7 @@
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
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 {StorageProvider} from '../storage/storage.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])));
const setting: SCSetting = await settingsProvider
.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 () => {
await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0])));
const value = await settingsProvider
.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 () => {
@@ -79,7 +79,7 @@ describe('SettingsProvider', () => {
storageProviderSpy.get.and.returnValue(Promise.resolve([]));
const value = await settingsProvider
.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 () => {
@@ -108,10 +108,10 @@ describe('SettingsProvider', () => {
const name = CONFIG_SETTINGS_MOCK[0].name;
await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0])));
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
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 () => {
@@ -134,7 +134,7 @@ describe('SettingsProvider', () => {
await settingsProvider.resetDefault();
const value = await settingsProvider
.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 () => {
@@ -172,7 +172,7 @@ describe('SettingsProvider', () => {
await testValue(CONFIG_SETTINGS_MOCK[6], '');
await testValue(CONFIG_SETTINGS_MOCK[6], 123456);
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) {
@@ -192,10 +192,8 @@ describe('SettingsProvider', () => {
const CONFIG_SETTINGS_MOCK: SCSetting[] = [
{
categories: ['credentials'],
input: {
defaultValue: '',
inputType: 'text',
},
defaultValue: '',
inputType: SCSettingInputType.Text,
name: 'username',
order: 0,
origin: {
@@ -219,10 +217,8 @@ describe('SettingsProvider', () => {
{
categories: ['credentials'],
description: '',
input: {
defaultValue: '',
inputType: 'password',
},
defaultValue: '',
inputType: SCSettingInputType.Password,
name: 'password',
order: 1,
origin: {
@@ -246,10 +242,8 @@ describe('SettingsProvider', () => {
{
categories: ['profile'],
description: '',
input: {
defaultValue: 0,
inputType: 'number',
},
defaultValue: 0,
inputType: SCSettingInputType.Number,
name: 'age',
order: 0,
origin: {
@@ -273,11 +267,8 @@ describe('SettingsProvider', () => {
{
categories: ['profile'],
description: '',
input: {
defaultValue: 'student',
inputType: 'singleChoice',
values: ['student', 'employee', 'guest'],
},
defaultValue: 'student',
inputType: SCSettingInputType.SingleChoice,
name: 'group',
order: 1,
origin: {
@@ -301,15 +292,13 @@ describe('SettingsProvider', () => {
},
type: SCThingType.Setting,
uid: '',
values: ['student', 'employee', 'guest'],
},
{
categories: ['profile'],
description: '',
input: {
defaultValue: 'en',
inputType: 'singleChoice',
values: ['en', 'de'],
},
defaultValue: 'en',
inputType: SCSettingInputType.SingleChoice,
name: 'language',
order: 0,
origin: {
@@ -331,15 +320,13 @@ describe('SettingsProvider', () => {
},
type: SCThingType.Setting,
uid: '',
values: ['en', 'de'],
},
{
categories: ['privacy'],
description: '',
input: {
defaultValue: false,
inputType: 'singleChoice',
values: [true, false],
},
defaultValue: false,
inputType: SCSettingInputType.SingleChoice,
name: 'geoLocation',
order: 0,
origin: {
@@ -363,15 +350,13 @@ describe('SettingsProvider', () => {
},
type: SCThingType.Setting,
uid: '',
values: [true, false],
},
{
categories: ['others'],
description: '',
input: {
defaultValue: [],
inputType: 'multipleChoice',
values: [1, 2, 3, 4, 5, 6, 7, 8],
},
defaultValue: [],
inputType: SCSettingInputType.MultipleChoice,
name: 'numbers',
order: 0,
origin: {
@@ -393,6 +378,7 @@ describe('SettingsProvider', () => {
},
type: SCThingType.Setting,
uid: '',
values: [1, 2, 3, 4, 5, 6, 7, 8],
},
];
});

View File

@@ -16,25 +16,29 @@ import {Injectable} from '@angular/core';
import {Geolocation} from '@ionic-native/geolocation/ngx';
import {
SCSetting,
SCSettingMultipleChoice,
SCSettingSingleChoice,
SCSettingValue,
SCSettingValues,
} from '@openstapps/core';
import {Logger} from '@openstapps/logger';
import * as deepMerge from 'deepmerge';
import {ConfigProvider} from '../config/config.provider';
import {StorageProvider} from '../storage/storage.provider';
export const STORAGE_KEY_SETTINGS = 'settings';
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
*/
export interface CategoryWithSettings {
/**
* Category name
*/
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
*
*/
@Injectable()
export class SettingsProvider {
/**
* Order of the setting categories
*/
categoriesOrder: string[];
/**
* Is provider initialized
*/
initialized = false;
logger = new Logger();
/**
* Cache for the imported settings
*/
settingsCache: SettingsCache;
/**
* Return true if all given values are valid to possible values in given settingInput
* @param settingInput
* @param values
* @param possibleValues Possible values
* @param enteredValues Entered value
*/
public static checkMultipleChoiceValue(settingInput: SCSettingMultipleChoice, values: SCSettingValue[]): boolean {
for (const value of values) {
if (!settingInput.values.includes(value)) {
public static checkMultipleChoiceValue(
possibleValues: SCSettingValues | undefined,
enteredValues: SCSettingValues,
): boolean {
if ( typeof possibleValues === 'undefined' ) {
return false;
}
for (const value of enteredValues) {
if (!possibleValues.includes(value)) {
return false;
}
}
return true;
}
/**
* Returns true if given value is valid to possible values in given settingInput
* @param settingInput
* @param value
* @param possibleValues Possible values
* @param enteredValue Entered value
*/
public static checkSingleChoiceValue(settingInput: SCSettingSingleChoice, value: SCSettingValue): boolean {
return settingInput.values !== undefined
&& settingInput.values.includes(value);
public static checkSingleChoiceValue(
possibleValues: SCSettingValues | undefined,
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 value value to validate
*/
public static validateValue(setting: SCSetting, value: any): boolean {
let isValueValid: boolean = false;
switch (setting.input.inputType) {
public static validateValue(setting: SCSetting, value: SCSettingValue | SCSettingValues): boolean {
let isValueValid = false;
switch (setting.inputType) {
case 'number':
if (typeof value === 'number') {
isValueValid = true;
}
break;
case 'multipleChoice':
if (!value.isArray) {
case 'multiple choice':
if (!Array.isArray(value)) {
isValueValid = false;
} else {
isValueValid = SettingsProvider.checkMultipleChoiceValue(setting.input, value);
isValueValid = SettingsProvider.checkMultipleChoiceValue(setting.values, value);
}
break;
case 'password':
@@ -118,32 +147,43 @@ export class SettingsProvider {
isValueValid = true;
}
break;
case 'singleChoice':
isValueValid = SettingsProvider.checkSingleChoiceValue(setting.input, value);
case 'single choice':
if (Array.isArray(value)) {
isValueValid = false;
} else {
isValueValid = SettingsProvider.checkSingleChoiceValue(setting.values, value);
}
break;
default:
}
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.settingsCache = {};
}
/**
* 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> {
if (!this.categoryExists(setting.categories[0])) {
await this.provideCategory(setting.categories[0]);
}
if (!this.settingExists(setting.categories[0], setting.name)) {
if (setting.input.value === undefined) {
setting.input.value = setting.input.defaultValue;
if (setting.value === undefined) {
setting.value = setting.defaultValue;
}
this.settingsCache[setting.categories[0]].settings[setting.name] = setting;
}
@@ -162,9 +202,10 @@ export class SettingsProvider {
settingValuesContainer[categoryKey] = {};
}
settingValuesContainer[categoryKey][settingKey] =
this.settingsCache[categoryKey].settings[settingKey].input.value;
this.settingsCache[categoryKey].settings[settingKey].value;
}
}
return settingValuesContainer;
}
@@ -196,11 +237,11 @@ export class SettingsProvider {
// if saved setting value exists set it, otherwise set to default value
if (typeof valuesContainer[categoryKey] !== 'undefined'
&& typeof valuesContainer[categoryKey][settingKey] !== 'undefined') {
this.settingsCache[categoryKey].settings[settingKey].input.value =
this.settingsCache[categoryKey].settings[settingKey].value =
valuesContainer[categoryKey][settingKey];
} else {
this.settingsCache[categoryKey].settings[settingKey].input.value =
this.settingsCache[categoryKey].settings[settingKey].input.defaultValue;
this.settingsCache[categoryKey].settings[settingKey].value =
this.settingsCache[categoryKey].settings[settingKey].defaultValue;
}
}
}
@@ -228,7 +269,7 @@ export class SettingsProvider {
/**
* Returns true if category exists
* @param category
* @param category Category key name
*/
public categoryExists(category: string): boolean {
return this.settingsCache[category] !== undefined;
@@ -256,6 +297,7 @@ export class SettingsProvider {
}
}
}
return true;
}
@@ -264,6 +306,7 @@ export class SettingsProvider {
*/
public async getCache(): Promise<SettingsCache> {
await this.init();
return JSON.parse(JSON.stringify(this.settingsCache));
}
@@ -281,14 +324,13 @@ export class SettingsProvider {
*
* @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();
if (this.settingExists(category, name)) {
// return a copy of the settings
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();
if (this.settingExists(category, name)) {
// return a copy of the settings value
return JSON.parse(JSON.stringify(this.settingsCache[category].settings[name].input.value));
} else {
throw new Error(`Setting "${name}" not provided`);
return JSON.parse(JSON.stringify(this.settingsCache[category].settings[name].value));
}
throw new Error(`Setting "${name}" not provided`);
}
/**
@@ -340,7 +381,7 @@ export class SettingsProvider {
async resetDefault(): Promise<void> {
for (const catKey of Object.keys(this.settingsCache)) {
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;
}
}
@@ -373,24 +414,24 @@ export class SettingsProvider {
/**
* Sets a valid value of a setting and persists changes in storage
* @param category
* @param name
* @param value
* @param category Category key name
* @param name Setting key name
* @param value Value to be set
*
* @throws Exception if setting is not provided or value not valid to the settings inputType
*/
public async setSettingValue(category: string, name: string,
value: any): Promise<void> {
value: SCSettingValue | SCSettingValues): Promise<void> {
await this.init();
if (this.settingExists(category, name)) {
const setting: SCSetting = this.settingsCache[category].settings[name];
const isValueValid = SettingsProvider.validateValue(setting, value);
if (isValueValid) {
this.settingsCache[category].settings[name].input.value = value;
this.settingsCache[category].settings[name].value = value;
await this.saveSettingValues();
} else {
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 {
throw new Error(`setting ${name} is not provided`);
@@ -399,8 +440,8 @@ export class SettingsProvider {
/**
* Returns true if setting in category exists
* @param category
* @param setting
* @param category Category key name
* @param setting Setting key name
*/
public settingExists(category: string, setting: string): boolean {
return this.categoryExists(category) && this.settingsCache[category].settings[setting] !== undefined;

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
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -12,16 +12,19 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { NgModule } from '@angular/core';
import { IonicStorageModule } from '@ionic/storage';
import { StorageProvider } from './storage.provider';
import {NgModule} from '@angular/core';
import {IonicStorageModule} from '@ionic/storage';
import {StorageProvider} from './storage.provider';
/**
* Angular storage provider module
*/
@NgModule({
imports: [
IonicStorageModule.forRoot()
IonicStorageModule.forRoot(),
],
providers: [
StorageProvider
]
StorageProvider,
],
})
export class StorageModule {}

View File

@@ -20,24 +20,29 @@ import {Storage} from '@ionic/storage';
*/
@Injectable()
export class StorageProvider {
constructor(private storage: Storage) {
}
/**
* Initializes the storage (waits until it's ready)
*
* @param storage TODO
*/
async init(): Promise<any> {
return this.storage.ready();
constructor(private readonly storage: Storage) {
}
/**
* 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 value Resource to store under the key
* @param keys Unique identifiers of the resources for deletion
*/
async put<T>(key: string, value: T): Promise<T> {
return this.storage.set(key, value);
async delete(...keys: string[]): Promise<void> {
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) {
throw new Error('Value not found.');
}
return entry;
}
// tslint:disable:no-any
/**
* Gets values from the storage using the provided pattern
*
* @param pattern Regular expression or text to test existing storage keys with
* Retrieves all the storage entries
*/
async search<T>(pattern: RegExp | string): Promise<Map<string, T>> {
async getAll(): Promise<Map<string, any>> {
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) => {
if (checkIt(pattern, key)) {
map.set(key, value);
}
map.set(key, value);
});
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
*
@@ -127,34 +87,10 @@ export class StorageProvider {
gets.push(getToMap(key));
});
await Promise.all(gets);
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
*
@@ -163,4 +99,78 @@ export class StorageProvider {
async has(key: string): Promise<boolean> {
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;
}
}