feat(menu): add context menu

Closes #3
This commit is contained in:
Sebastian Lange
2019-05-27 16:38:47 +02:00
parent 3ce3c9ba16
commit 1dbf4515fe
27 changed files with 2261 additions and 767 deletions

View File

@@ -17,13 +17,13 @@ import {AlertController} from '@ionic/angular';
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
import {
SCLanguage,
SCLanguageCode,
SCSetting,
SCSettingValue,
SCSettingValues,
SCThingTranslator,
SCTranslations,
} from '@openstapps/core';
import {Logger} from '@openstapps/logger';
import {SettingsProvider} from '../settings.provider';
/**
@@ -34,6 +34,12 @@ import {SettingsProvider} from '../settings.provider';
templateUrl: 'settings-item.html',
})
export class SettingsItemComponent implements OnInit {
/**
* If set the setting will be shown as compact view
*/
@Input() compactView = false;
/**
* Flag for workaround for selected 'select option' not updating translation
*/
@@ -107,8 +113,6 @@ export class SettingsItemComponent implements OnInit {
* NgInit
*/
ngOnInit(): void {
Logger.log(JSON.stringify(this.setting));
this.translatedSetting = this.translator.translate(this.setting);
}
@@ -136,7 +140,7 @@ export class SettingsItemComponent implements OnInit {
// handle general settings, with special actions
switch (this.setting.name) {
case 'language':
this.translateService.use(this.setting.value.toString());
this.translateService.use(this.setting.value as SCLanguageCode);
break;
case 'geoLocation':
if (!!this.setting.value) {
@@ -146,7 +150,9 @@ export class SettingsItemComponent implements OnInit {
default:
}
await this.settingsProvider
.setSettingValue(this.setting.categories[0], this.setting.name, this.setting.value);
.setSettingValue(this.setting.categories[0],
this.setting.name,
this.setting.value);
} else {
// reset setting
this.setting.value =

View File

@@ -1,9 +1,12 @@
<ion-card *ngIf="setting">
<ion-card-header>
<span>{{ translatedSetting.name() | titlecase }}</span>
<span>
{{ translatedSetting.name() | titlecase }}
<ion-icon *ngIf="compactView" name="information-circle-outline" (click)="presentAlert(translator.translate(setting).name(), translator.translate(setting).description())"></ion-icon>
</span>
</ion-card-header>
<ion-card-content>
<ion-note>{{ translatedSetting.description() }}</ion-note>
<ion-note *ngIf="!compactView">{{ translatedSetting.description() }}</ion-note>
<div [ngSwitch]="setting.inputType" *ngIf="isVisible" >
<ion-item *ngSwitchCase="'number'">

View File

@@ -12,12 +12,11 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Component} from '@angular/core';
import {ChangeDetectorRef, Component} from '@angular/core';
import {AlertController, ToastController} from '@ionic/angular';
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
import {SCLanguage, SCSettingMeta, SCThingTranslator, SCTranslations} from '@openstapps/core';
import {SettingsCache, SettingsProvider} from '../settings.provider';
/**
* Settings page component
*/
@@ -59,11 +58,13 @@ export class SettingsPageComponent {
* @param settingsProvider SettingsProvider
* @param toastController ToastController
* @param translateService TranslateService
* @param changeDetectorRef ChangeDetectorRef
*/
constructor(private readonly alertController: AlertController,
private readonly settingsProvider: SettingsProvider,
private readonly toastController: ToastController,
private readonly translateService: TranslateService) {
private readonly translateService: TranslateService,
private readonly changeDetectorRef: ChangeDetectorRef) {
this.language = translateService.currentLang as keyof SCTranslations<SCLanguage>;
this.translator = new SCThingTranslator(this.language);
@@ -72,8 +73,6 @@ export class SettingsPageComponent {
this.translator = new SCThingTranslator(this.language);
});
this.settingsCache = {};
this.categoriesOrder = settingsProvider.getCategoriesOrder();
}
/**
@@ -93,6 +92,9 @@ export class SettingsPageComponent {
*/
async loadSettings(): Promise<void> {
this.settingsCache = await this.settingsProvider.getCache();
// categoriesOrder triggers updating the View, because it is used in the ngFor loop
this.categoriesOrder = this.settingsProvider.getCategoriesOrder();
this.changeDetectorRef.detectChanges();
}
/**

View File

@@ -17,7 +17,7 @@ import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {RouterModule, Routes} from '@angular/router';
import {Geolocation} from '@ionic-native/geolocation/ngx';
import {IonicModule} from '@ionic/angular';
import {Events, IonicModule} from '@ionic/angular';
import {TranslateModule} from '@ngx-translate/core';
import {ConfigProvider} from '../config/config.provider';
@@ -37,6 +37,9 @@ const settingsRoutes: Routes = [
SettingsPageComponent,
SettingsItemComponent,
],
exports: [
SettingsItemComponent,
],
imports: [
CommonModule,
FormsModule,
@@ -46,6 +49,7 @@ const settingsRoutes: Routes = [
],
providers: [
ConfigProvider,
Events,
Geolocation,
SettingsProvider,
],

View File

@@ -14,12 +14,13 @@
*/
import {Injectable} from '@angular/core';
import {Geolocation} from '@ionic-native/geolocation/ngx';
import {Events} from '@ionic/angular';
import {
SCSetting,
SCSettingValue,
SCSettingValues,
} from '@openstapps/core';
import deepmerge from 'deepmerge';
import deepMerge from 'deepmerge';
import {ConfigProvider} from '../config/config.provider';
import {StorageProvider} from '../storage/storage.provider';
@@ -164,10 +165,12 @@ export class SettingsProvider {
* @param storage TODO
* @param configProvider TODO
* @param geoLocation TODO
* @param events TODO
*/
constructor(private readonly storage: StorageProvider,
private readonly configProvider: ConfigProvider,
private readonly geoLocation: Geolocation) {
private readonly geoLocation: Geolocation,
private readonly events: Events) {
this.categoriesOrder = [];
this.settingsCache = {};
}
@@ -246,8 +249,9 @@ export class SettingsProvider {
}
await this.saveSettingValues();
}
this.initialized = true;
// publish provider initialised
this.events.publish('stapps.settings.initialised');
}
/**
@@ -339,7 +343,7 @@ export class SettingsProvider {
*
* @throws Exception if setting is not provided
*/
public async getValue(category: string, name: string): Promise<unknown> {
public async getValue(category: string, name: string): Promise<SCSettingValue | SCSettingValues> {
await this.init();
if (this.settingExists(category, name)) {
// return a copy of the settings value
@@ -395,7 +399,7 @@ export class SettingsProvider {
const savedSettingsValues: SettingValuesContainer =
await this.storage.get<SettingValuesContainer>(STORAGE_KEY_SETTING_VALUES);
const cacheSettingsValues = this.getSettingValuesFromCache();
const mergedSettingValues = deepmerge(savedSettingsValues, cacheSettingsValues);
const mergedSettingValues = deepMerge(savedSettingsValues, cacheSettingsValues);
await this.storage
.put<SettingValuesContainer>(STORAGE_KEY_SETTING_VALUES, mergedSettingValues);
} else {
@@ -412,7 +416,8 @@ 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. Also the changes get published bey Events
*
* @param category Category key name
* @param name Setting key name
* @param value Value to be set
@@ -426,8 +431,11 @@ export class SettingsProvider {
const setting: SCSetting = this.settingsCache[category].settings[name];
const isValueValid = SettingsProvider.validateValue(setting, value);
if (isValueValid) {
// set and persist new value
this.settingsCache[category].settings[name].value = value;
await this.saveSettingValues();
// publish setting changes
this.events.publish('stapps.settings.changed', category, name, value);
} else {
throw new Error(`Value "${value}" of type
${typeof value} is not valid for ${setting.inputType}`);