Resolve "Dashboard navigation icons are saved in user preferences"

This commit is contained in:
Thea Schöbl
2022-09-08 14:07:05 +00:00
committed by Rainer Killinger
parent d571b1dbe5
commit 37dd29a60f
13 changed files with 202 additions and 103 deletions

View File

@@ -1,16 +1,16 @@
/* /*
* Copyright (C) 2022 StApps * Copyright (C) 2022 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details. * more details.
* *
* 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} from '@angular/common'; import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core'; import {NgModule} from '@angular/core';
@@ -18,7 +18,7 @@ import {FormsModule} from '@angular/forms';
import {RouterModule, Routes} from '@angular/router'; import {RouterModule, Routes} from '@angular/router';
import {IonicModule} from '@ionic/angular'; import {IonicModule} from '@ionic/angular';
import {SwiperModule} from 'swiper/angular'; import {SwiperModule} from 'swiper/angular';
import {TranslateModule} from '@ngx-translate/core'; import {TranslateModule, TranslatePipe} from '@ngx-translate/core';
import {MomentModule} from 'ngx-moment'; import {MomentModule} from 'ngx-moment';
import {DataModule} from '../data/data.module'; import {DataModule} from '../data/data.module';
import {SettingsProvider} from '../settings/settings.provider'; import {SettingsProvider} from '../settings/settings.provider';
@@ -70,6 +70,6 @@ const catalogRoutes: Routes = [
ThingTranslateModule.forChild(), ThingTranslateModule.forChild(),
UtilModule, UtilModule,
], ],
providers: [SettingsProvider], providers: [SettingsProvider, TranslatePipe],
}) })
export class DashboardModule {} export class DashboardModule {}

View File

@@ -1,4 +1,25 @@
/*
* Copyright (C) 2022 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/>.
*/
export enum EditModalTypeEnum { export enum EditModalTypeEnum {
CHECKBOXES, CHECKBOXES,
RADIOBOXES, RADIOBOXES,
} }
export interface EditModalItem {
id: unknown;
labelLocalized: string;
active: boolean;
}

View File

@@ -1,27 +1,30 @@
<!-- <!--
~ Copyright (C) 2022 StApps ~ Copyright (C) 2022 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.
~ ~
~ This program is distributed in the hope that it will be useful, but WITHOUT ~ This program is distributed in the hope that it will be useful, but WITHOUT
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
~ more details. ~ more details.
~ ~
~ 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/>.
--> -->
<ion-header translucent> <ion-header>
<ion-toolbar mode="ios"> <ion-toolbar mode="ios">
<ion-title>{{ 'modal.settings' | translate }}</ion-title> <ion-title>{{ 'modal.settings' | translate | titlecase }}</ion-title>
<ion-button fill="clear" slot="end" (click)="dismissModal()"> <ion-button fill="clear" slot="start" (click)="dismissModal()">
<ion-icon name="close"></ion-icon> {{ 'modal.DISMISS_CANCEL' | translate }}
</ion-button>
<ion-button fill="clear" slot="end" (click)="onSaveClick()">
<ion-label>{{ 'modal.DISMISS_CONFIRM' | translate }}</ion-label>
</ion-button> </ion-button>
</ion-toolbar> </ion-toolbar>
</ion-header> </ion-header>
<ion-content fullscreen> <ion-content>
<ng-container [ngSwitch]="true"> <ng-container [ngSwitch]="true">
<ion-reorder-group <ion-reorder-group
*ngSwitchCase="type === types.CHECKBOXES" *ngSwitchCase="type === types.CHECKBOXES"
@@ -29,9 +32,9 @@
(ionItemReorder)="doReorder($event)" (ionItemReorder)="doReorder($event)"
> >
<!-- Default reorder icon, end aligned items --> <!-- Default reorder icon, end aligned items -->
<ion-item *ngFor="let item of reorderedItems"> <ion-item *ngFor="let item of items">
<ion-reorder slot="start"></ion-reorder> <ion-reorder slot="start"></ion-reorder>
<ion-label>{{ item.label | translate }}</ion-label> <ion-label>{{ item.labelLocalized }}</ion-label>
<ion-toggle <ion-toggle
slot="end" slot="end"
[checked]="item.active" [checked]="item.active"
@@ -50,17 +53,9 @@
}}</ion-label> }}</ion-label>
</ion-list-header> </ion-list-header>
<ion-item *ngFor="let item of items"> <ion-item *ngFor="let item of items">
<ion-label>{{ item.name }}</ion-label> <ion-label>{{ item.labelLocalized }}</ion-label>
<ion-radio slot="end" [value]="item.uid"></ion-radio> <ion-radio slot="end" [value]="item.id"></ion-radio>
</ion-item> </ion-item>
</ion-radio-group> </ion-radio-group>
</ng-container> </ng-container>
</ion-content> </ion-content>
<ion-footer class="ion-text-end">
<ion-button fill="clear" class="ion-margin-end" (click)="onSaveClick()">{{
'save' | translate
}}</ion-button>
<ion-button fill="clear" (click)="dismissModal()">{{
'abort' | translate
}}</ion-button>
</ion-footer>

View File

@@ -15,9 +15,7 @@
import {Component, Input, OnInit, ViewChild} from '@angular/core'; import {Component, Input, OnInit, ViewChild} from '@angular/core';
import {IonReorderGroup, ModalController} from '@ionic/angular'; import {IonReorderGroup, ModalController} from '@ionic/angular';
import {ItemReorderEventDetail} from '@ionic/core'; import {ItemReorderEventDetail} from '@ionic/core';
import {SCThings} from '@openstapps/core'; import {EditModalItem, EditModalTypeEnum} from './edit-modal-type.enum';
import {MenuItemInterface} from '../sections/navigation-section/menu-item.interface';
import {EditModalTypeEnum} from './edit-modal-type.enum';
/** /**
* Shows a modal window to sort and enable/disable menu items * Shows a modal window to sort and enable/disable menu items
@@ -32,11 +30,11 @@ export class EditModalComponent implements OnInit {
@Input() type: EditModalTypeEnum = EditModalTypeEnum.CHECKBOXES; @Input() type: EditModalTypeEnum = EditModalTypeEnum.CHECKBOXES;
@Input() items: MenuItemInterface[] | SCThings[]; @Input() items: EditModalItem[];
@Input() selectedValue: string; @Input() selectedValue: string;
reorderedItems: MenuItemInterface[] | SCThings[]; reorderedItems: EditModalItem[];
types = EditModalTypeEnum; types = EditModalTypeEnum;

View File

@@ -1,6 +1,30 @@
/*
* Copyright (C) 2022 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/>.
*/
export interface MenuItemInterface { export interface MenuItemInterface {
icon: string; icon: string;
label: string; label: string;
link: string; link: string;
active: boolean;
} }
export enum MenuItemKey {
CATALOG = 'catalog',
CANTEEN = 'canteen',
MAP = 'map',
SETTINGS = 'settings',
SEARCH = 'search',
}
export type MenuItemConfig = Record<MenuItemKey, boolean>;

View File

@@ -1,50 +1,53 @@
/* /*
* Copyright (C) 2022 StApps * Copyright (C) 2022 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details. * more details.
* *
* 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 {MenuItemInterface} from './menu-item.interface'; import {MenuItemInterface, MenuItemKey} from './menu-item.interface';
import {SCIcon} from '../../../../util/ion-icon/icon'; import {SCIcon} from '../../../../util/ion-icon/icon';
export const MenuItems: MenuItemInterface[] = [ export const MENU_ITEMS: Record<MenuItemKey, MenuItemInterface> = {
{ catalog: {
icon: SCIcon`book`, icon: SCIcon`book`,
label: 'dashboard.navigation.item.catalog', label: 'dashboard.navigation.item.catalog',
link: '/catalog', link: '/catalog',
active: true,
}, },
{ canteen: {
icon: SCIcon`local_cafe`, icon: SCIcon`local_cafe`,
label: 'dashboard.navigation.item.canteen', label: 'dashboard.navigation.item.canteen',
link: '/canteen', link: '/canteen',
active: true,
}, },
{ map: {
icon: SCIcon`map`, icon: SCIcon`map`,
label: 'dashboard.navigation.item.map', label: 'dashboard.navigation.item.map',
link: '/map', link: '/map',
active: true,
}, },
{ settings: {
icon: SCIcon`settings`, icon: SCIcon`settings`,
label: 'dashboard.navigation.item.settings', label: 'dashboard.navigation.item.settings',
link: '/settings', link: '/settings',
active: true,
}, },
{ search: {
icon: SCIcon`search`, icon: SCIcon`search`,
label: 'dashboard.navigation.item.search', label: 'dashboard.navigation.item.search',
link: '/search', link: '/search',
active: false,
}, },
};
export const DEFAULT_ACTIVE_MENU_ITEMS: MenuItemKey[] = [
MenuItemKey.CATALOG,
MenuItemKey.CANTEEN,
MenuItemKey.MAP,
MenuItemKey.SETTINGS,
MenuItemKey.SEARCH,
]; ];

View File

@@ -1,16 +1,16 @@
<!-- <!--
~ Copyright (C) 2022 StApps ~ Copyright (C) 2022 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.
~ ~
~ This program is distributed in the hope that it will be useful, but WITHOUT ~ This program is distributed in the hope that it will be useful, but WITHOUT
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
~ more details. ~ more details.
~ ~
~ 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/>.
--> -->
<stapps-section <stapps-section
@@ -24,10 +24,10 @@
slidesPerView="auto" slidesPerView="auto"
class="navigation-swiper card-swiper" class="navigation-swiper card-swiper"
> >
<ng-template swiperSlide *ngFor="let menuItem of activeMenuItems"> <ng-template swiperSlide *ngFor="let item of activeMenuItems">
<a [routerLink]="menuItem.link" class="card"> <a [routerLink]="menuItems[item].link" class="card">
<ion-icon size="40" [name]="menuItem.icon"></ion-icon> <ion-icon size="40" [name]="menuItems[item].icon"></ion-icon>
<ion-label>{{ menuItem.label | translate }}</ion-label> <ion-label>{{ menuItems[item].label | translate }}</ion-label>
</a> </a>
</ng-template> </ng-template>
</swiper> </swiper>

View File

@@ -13,12 +13,16 @@
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {Component, OnInit, ViewEncapsulation} from '@angular/core'; import {Component, OnInit, ViewEncapsulation} from '@angular/core';
import {ModalController} from '@ionic/angular'; import {IonRouterOutlet, ModalController} from '@ionic/angular';
import {EditModalComponent} from '../../edit-modal/edit-modal.component'; import {EditModalComponent} from '../../edit-modal/edit-modal.component';
import {MenuItems} from './menu-items.config'; import {DEFAULT_ACTIVE_MENU_ITEMS, MENU_ITEMS} from './menu-items.config';
import {MenuItemInterface} from './menu-item.interface'; import {MenuItemKey} from './menu-item.interface';
import {EditModalTypeEnum} from '../../edit-modal/edit-modal-type.enum'; import {
EditModalItem,
EditModalTypeEnum,
} from '../../edit-modal/edit-modal-type.enum';
import {StorageProvider} from '../../../storage/storage.provider'; import {StorageProvider} from '../../../storage/storage.provider';
import {TranslatePipe} from '@ngx-translate/core';
const DASHBOARD_NAVIGATION = 'stapps.dashboard.navigation'; const DASHBOARD_NAVIGATION = 'stapps.dashboard.navigation';
@@ -44,13 +48,15 @@ export class NavigationSectionComponent implements OnInit {
width: 120, width: 120,
}; };
menuItems: MenuItemInterface[] = MenuItems; menuItems = MENU_ITEMS;
activeMenuItems: MenuItemInterface[] = MenuItems; activeMenuItems: MenuItemKey[] = DEFAULT_ACTIVE_MENU_ITEMS;
constructor( constructor(
public modalController: ModalController, public modalController: ModalController,
private storageProvider: StorageProvider, private storageProvider: StorageProvider,
private translatePipe: TranslatePipe,
private routerOutlet: IonRouterOutlet,
) {} ) {}
ngOnInit() { ngOnInit() {
@@ -67,9 +73,11 @@ export class NavigationSectionComponent implements OnInit {
); );
if (storedMenuItems) { if (storedMenuItems) {
const parsedMenuItems = JSON.parse(storedMenuItems); const parsedMenuItems = JSON.parse(storedMenuItems);
if (Array.isArray(parsedMenuItems)) { if (
this.menuItems = parsedMenuItems; Array.isArray(parsedMenuItems) &&
this.activeMenuItems = parsedMenuItems.filter(item => item.active); parsedMenuItems.every(it => typeof it === 'string')
) {
this.activeMenuItems = parsedMenuItems;
} }
} }
} }
@@ -77,15 +85,12 @@ export class NavigationSectionComponent implements OnInit {
/** /**
* Save updated order of items * Save updated order of items
*
* @param items List of items
*/ */
setItems(items: MenuItemInterface[]) { updateActiveItems(items: MenuItemKey[]) {
this.menuItems = items; this.activeMenuItems = items;
this.activeMenuItems = items.filter(item => item.active);
void this.storageProvider.put<string>( void this.storageProvider.put<string>(
DASHBOARD_NAVIGATION, DASHBOARD_NAVIGATION,
JSON.stringify(items), JSON.stringify(this.activeMenuItems),
); );
} }
@@ -95,8 +100,14 @@ export class NavigationSectionComponent implements OnInit {
async onSectionEdit() { async onSectionEdit() {
const modal = await this.modalController.create({ const modal = await this.modalController.create({
component: EditModalComponent, component: EditModalComponent,
canDismiss: true,
presentingElement: this.routerOutlet.nativeEl,
componentProps: { componentProps: {
items: this.menuItems, items: Object.entries(this.menuItems).map(([id, item]) => ({
id,
active: this.activeMenuItems.includes(id as MenuItemKey),
labelLocalized: this.translatePipe.transform(item.label),
})),
type: EditModalTypeEnum.CHECKBOXES, type: EditModalTypeEnum.CHECKBOXES,
}, },
}); });
@@ -104,7 +115,11 @@ export class NavigationSectionComponent implements OnInit {
modal.onDidDismiss().then(result => { modal.onDidDismiss().then(result => {
if (result.data?.items) { if (result.data?.items) {
this.setItems(result.data.items); this.updateActiveItems(
result.data.items
.filter((it: EditModalItem) => it.active)
.map((it: EditModalItem) => it.id),
);
} }
}); });
} }

View File

@@ -1,16 +1,16 @@
/* /*
* Copyright (C) 2022 StApps * Copyright (C) 2022 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details. * more details.
* *
* 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';
@@ -19,6 +19,7 @@ import {IonIconDirective} from './ion-icon.directive';
import {IonBackButtonDirective} from './ion-back-button.directive'; import {IonBackButtonDirective} from './ion-back-button.directive';
import {IonSearchbarDirective} from './ion-searchbar.directive'; import {IonSearchbarDirective} from './ion-searchbar.directive';
import {IonBreadcrumbDirective} from './ion-breadcrumb.directive'; import {IonBreadcrumbDirective} from './ion-breadcrumb.directive';
import {IonReorderDirective} from './ion-reorder.directive';
@NgModule({ @NgModule({
declarations: [ declarations: [
@@ -27,9 +28,11 @@ import {IonBreadcrumbDirective} from './ion-breadcrumb.directive';
IonBackButtonDirective, IonBackButtonDirective,
IonSearchbarDirective, IonSearchbarDirective,
IonBreadcrumbDirective, IonBreadcrumbDirective,
IonReorderDirective,
], ],
exports: [ exports: [
IonIconDirective, IonIconDirective,
IonReorderDirective,
IonBackButtonDirective, IonBackButtonDirective,
IonSearchbarDirective, IonSearchbarDirective,
IonBreadcrumbDirective, IonBreadcrumbDirective,

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2022 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/>.
*/
import {Directive, ElementRef, ViewContainerRef} from '@angular/core';
import {SCIcon} from './icon';
import {IconReplacer} from './replace-util';
@Directive({
selector: 'ion-reorder',
})
export class IonReorderDirective extends IconReplacer {
constructor(element: ElementRef, viewContainerRef: ViewContainerRef) {
super(element, viewContainerRef, 'shadow');
}
replace() {
this.replaceIcon(this.host, {
name: SCIcon`reorder`,
size: 24,
});
}
}

View File

@@ -7,6 +7,9 @@
"share": "Teilen", "share": "Teilen",
"timeSuffix": "Uhr", "timeSuffix": "Uhr",
"modal": { "modal": {
"DISMISS_NEUTRAL": "Schließen",
"DISMISS_CANCEL": "Abbrechen",
"DISMISS_CONFIRM": "Bestätigen",
"DISMISS": "Schließen", "DISMISS": "Schließen",
"settings": "Einstellungen" "settings": "Einstellungen"
}, },

View File

@@ -7,6 +7,9 @@
"share": "Share", "share": "Share",
"timeSuffix": "", "timeSuffix": "",
"modal": { "modal": {
"DISMISS_NEUTRAL": "Close",
"DISMISS_CANCEL": "Cancel",
"DISMISS_CONFIRM": "Confirm",
"DISMISS": "Close", "DISMISS": "Close",
"settings": "Settings" "settings": "Settings"
}, },

Binary file not shown.