From 23bd5a431c24a8140f1f0acedeb9268ab49304f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thea=20Sch=C3=B6bl?= Date: Wed, 22 Mar 2023 15:33:07 +0000 Subject: [PATCH] refactor: simplify favorites and mensa dashboard sections --- src/app/_helpers/rxjs/mutation-observer.ts | 34 +++++ .../dashboard/dashboard.component.scss | 3 + src/app/modules/dashboard/dashboard.module.ts | 3 - .../edit-modal/edit-modal.component.html | 52 -------- .../edit-modal/edit-modal.component.scss | 3 - .../edit-modal/edit-modal.component.ts | 65 --------- ...t-modal-type.enum.ts => fade.animation.ts} | 16 +-- src/app/modules/dashboard/mensa-filters.ts | 28 ++++ .../favorites-section.component.html | 4 +- .../favorites-section.component.ts | 116 ++-------------- .../mensa-section-content.component.ts | 11 +- .../mensa-section.component.html | 2 +- .../mensa-section/mensa-section.component.ts | 126 ++---------------- .../news-section/news-section.component.scss | 1 - .../news-section/news-section.component.ts | 27 ++-- .../search-section.component.html | 2 +- .../search-section.component.ts | 31 +---- .../modules/favorites/favorites.service.ts | 2 + .../profile/page/profile-page-section.html | 1 + .../profile/page/profile-page-section.scss | 4 +- src/app/util/ion-icon/icon.component.ts | 23 ++-- src/app/util/section.component.html | 16 ++- src/app/util/section.component.scss | 1 + src/app/util/section.component.ts | 59 ++++---- src/app/util/simple-swiper.component.ts | 9 +- src/app/util/simple-swiper.scss | 2 +- src/app/util/util.module.ts | 3 +- 27 files changed, 172 insertions(+), 472 deletions(-) create mode 100644 src/app/_helpers/rxjs/mutation-observer.ts delete mode 100644 src/app/modules/dashboard/edit-modal/edit-modal.component.html delete mode 100644 src/app/modules/dashboard/edit-modal/edit-modal.component.scss delete mode 100644 src/app/modules/dashboard/edit-modal/edit-modal.component.ts rename src/app/modules/dashboard/{edit-modal/edit-modal-type.enum.ts => fade.animation.ts} (71%) create mode 100644 src/app/modules/dashboard/mensa-filters.ts diff --git a/src/app/_helpers/rxjs/mutation-observer.ts b/src/app/_helpers/rxjs/mutation-observer.ts new file mode 100644 index 00000000..590081f1 --- /dev/null +++ b/src/app/_helpers/rxjs/mutation-observer.ts @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +import {Observable} from 'rxjs'; + +/** + * + */ +export function fromMutationObserver( + target: Node, + options?: MutationObserverInit, +): Observable { + return new Observable(subscriber => { + const observer = new MutationObserver(mutations => { + subscriber.next(mutations); + }); + observer.observe(target, options); + return () => { + observer.disconnect(); + }; + }); +} diff --git a/src/app/modules/dashboard/dashboard.component.scss b/src/app/modules/dashboard/dashboard.component.scss index 76cee475..ced3336f 100644 --- a/src/app/modules/dashboard/dashboard.component.scss +++ b/src/app/modules/dashboard/dashboard.component.scss @@ -50,6 +50,9 @@ ion-content { --background: var(--ion-color-light); --padding-bottom: var(--spacing-xl); + &::part(inner-scroll) { + scrollbar-gutter: stable; + } } .schedule { diff --git a/src/app/modules/dashboard/dashboard.module.ts b/src/app/modules/dashboard/dashboard.module.ts index 66919a61..e2d8d9a8 100644 --- a/src/app/modules/dashboard/dashboard.module.ts +++ b/src/app/modules/dashboard/dashboard.module.ts @@ -23,7 +23,6 @@ import {MomentModule} from 'ngx-moment'; import {DataModule} from '../data/data.module'; import {SettingsProvider} from '../settings/settings.provider'; import {DashboardComponent} from './dashboard.component'; -import {EditModalComponent} from './edit-modal/edit-modal.component'; import {SearchSectionComponent} from './sections/search-section/search-section.component'; import {NewsSectionComponent} from './sections/news-section/news-section.component'; import {MensaSectionComponent} from './sections/mensa-section/mensa-section.component'; @@ -46,7 +45,6 @@ const catalogRoutes: Routes = [ */ @NgModule({ declarations: [ - EditModalComponent, SearchSectionComponent, NewsSectionComponent, MensaSectionComponent, @@ -69,6 +67,5 @@ const catalogRoutes: Routes = [ NewsModule, ], providers: [SettingsProvider, TranslatePipe], - exports: [EditModalComponent], }) export class DashboardModule {} diff --git a/src/app/modules/dashboard/edit-modal/edit-modal.component.html b/src/app/modules/dashboard/edit-modal/edit-modal.component.html deleted file mode 100644 index 804342d8..00000000 --- a/src/app/modules/dashboard/edit-modal/edit-modal.component.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - {{ 'modal.settings' | translate | titlecase }} - - {{ 'modal.DISMISS_CANCEL' | translate }} - - - {{ 'modal.DISMISS_CONFIRM' | translate }} - - - - - - - - - - {{ item.labelLocalized }} - - - - - - - {{ 'dashboard.canteens.choose_favorite' | translate }} - - - {{ item.labelLocalized }} - - - - - diff --git a/src/app/modules/dashboard/edit-modal/edit-modal.component.scss b/src/app/modules/dashboard/edit-modal/edit-modal.component.scss deleted file mode 100644 index 8206b7d9..00000000 --- a/src/app/modules/dashboard/edit-modal/edit-modal.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -:host { - --width: 100vw; -} diff --git a/src/app/modules/dashboard/edit-modal/edit-modal.component.ts b/src/app/modules/dashboard/edit-modal/edit-modal.component.ts deleted file mode 100644 index acb4d47b..00000000 --- a/src/app/modules/dashboard/edit-modal/edit-modal.component.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 . - */ -import {Component, Input, OnInit, ViewChild} from '@angular/core'; -import {IonReorderGroup, ModalController} from '@ionic/angular'; -import {ItemReorderEventDetail} from '@ionic/core'; -import {EditModalItem, EditModalTypeEnum} from './edit-modal-type.enum'; - -/** - * Shows a modal window to sort and enable/disable menu items - */ -@Component({ - selector: 'stapps-dashboard-edit-modal', - templateUrl: 'edit-modal.component.html', - styleUrls: ['edit-modal.component.scss'], -}) -export class EditModalComponent implements OnInit { - @ViewChild(IonReorderGroup) reorderGroup: IonReorderGroup; - - @Input() type: EditModalTypeEnum = EditModalTypeEnum.CHECKBOXES; - - @Input() items: EditModalItem[]; - - @Input() selectedValue: string; - - reorderedItems: EditModalItem[]; - - types = EditModalTypeEnum; - - constructor(public modalController: ModalController) {} - - ngOnInit() { - this.reorderedItems = this.items; - } - - ionViewWillLeave() { - this.dismissModal(); - } - - doReorder(event: CustomEvent) { - this.reorderedItems = event.detail.complete(this.reorderedItems); - } - - onSaveClick() { - this.modalController.dismiss({ - items: this.reorderedItems, - selectedValue: this.selectedValue, - }); - } - - dismissModal() { - this.modalController.dismiss(); - } -} diff --git a/src/app/modules/dashboard/edit-modal/edit-modal-type.enum.ts b/src/app/modules/dashboard/fade.animation.ts similarity index 71% rename from src/app/modules/dashboard/edit-modal/edit-modal-type.enum.ts rename to src/app/modules/dashboard/fade.animation.ts index 0e2aacc3..d9c2dbbb 100644 --- a/src/app/modules/dashboard/edit-modal/edit-modal-type.enum.ts +++ b/src/app/modules/dashboard/fade.animation.ts @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 StApps + * Copyright (C) 2023 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,14 +12,8 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ +import {animate, style, transition, trigger} from '@angular/animations'; -export enum EditModalTypeEnum { - CHECKBOXES, - RADIOBOXES, -} - -export interface EditModalItem { - id: unknown; - labelLocalized: string; - active: boolean; -} +export const fadeAnimation = trigger('fade', [ + transition(':enter', [style({opacity: '0'}), animate(250, style({opacity: '1'}))]), +]); diff --git a/src/app/modules/dashboard/mensa-filters.ts b/src/app/modules/dashboard/mensa-filters.ts new file mode 100644 index 00000000..fbe73d61 --- /dev/null +++ b/src/app/modules/dashboard/mensa-filters.ts @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2023 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +import {SCBuildingCategories, SCThings, SCThingWithCategories} from '@openstapps/core'; + +const mensaCategories = new Set(['canteen', 'cafe', 'student canteen', 'restaurant']); + +/** + * + */ +export function isMensaThing(item: SCThings): boolean { + return ( + (item as SCThingWithCategories).categories?.some(category => + mensaCategories.has(category as never), + ) || false + ); +} diff --git a/src/app/modules/dashboard/sections/favorites-section/favorites-section.component.html b/src/app/modules/dashboard/sections/favorites-section/favorites-section.component.html index 33592964..ac771633 100644 --- a/src/app/modules/dashboard/sections/favorites-section/favorites-section.component.html +++ b/src/app/modules/dashboard/sections/favorites-section/favorites-section.component.html @@ -17,9 +17,9 @@ - + . */ -import {Component, OnInit} from '@angular/core'; -import {ActivatedRoute, Router} from '@angular/router'; -import {AlertController, AnimationController} from '@ionic/angular'; -import {combineLatest} from 'rxjs'; -import {debounceTime, distinctUntilChanged, startWith, take} from 'rxjs/operators'; -import {NGXLogger} from 'ngx-logger'; -import {SCThings} from '@openstapps/core'; - -import {DataProvider} from '../../../data/data.provider'; -import {DataRoutingService} from '../../../data/data-routing.service'; -import {SearchPageComponent} from '../../../data/list/search-page.component'; -import {PositionService} from '../../../map/position.service'; -import {SettingsProvider} from '../../../settings/settings.provider'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; +import {filter, map} from 'rxjs/operators'; import {FavoritesService} from '../../../favorites/favorites.service'; -import {ContextMenuService} from '../../../menu/context/context-menu.service'; -import {ConfigProvider} from '../../../config/config.provider'; +import {fadeAnimation} from '../../fade.animation'; +import {isMensaThing} from '../../mensa-filters'; /** * Shows a section with meals of the chosen mensa @@ -36,95 +25,14 @@ import {ConfigProvider} from '../../../config/config.provider'; selector: 'stapps-favorites-section', templateUrl: 'favorites-section.component.html', styleUrls: ['favorites-section.component.scss'], + animations: [fadeAnimation], + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class FavoritesSectionComponent extends SearchPageComponent implements OnInit { - constructor( - protected readonly alertController: AlertController, - protected dataProvider: DataProvider, - protected readonly contextMenuService: ContextMenuService, - protected readonly settingsProvider: SettingsProvider, - protected readonly logger: NGXLogger, - protected dataRoutingService: DataRoutingService, - protected router: Router, - route: ActivatedRoute, - positionService: PositionService, - private favoritesService: FavoritesService, - configProvider: ConfigProvider, - animationController: AnimationController, - ) { - super( - alertController, - dataProvider, - contextMenuService, - settingsProvider, - logger, - dataRoutingService, - router, - route, - positionService, - configProvider, - animationController, - ); - } +export class FavoritesSectionComponent { + items = this.favoritesService.favoriteThings$.pipe( + map(favorites => favorites.filter(it => !isMensaThing(it))), + filter(favorites => favorites.length > 0), + ); - async initialize() { - this.subscriptions.push( - combineLatest([ - this.queryTextChanged.pipe( - debounceTime(this.searchQueryDueTime), - distinctUntilChanged(), - startWith(this.queryText), - ), - this.favoritesService.favoritesChanged$, - ]).subscribe(async () => { - await this.fetchAndUpdateItems(); - this.queryChanged.next(); - }), - ); - } - - /** - * Fetches/updates the favorites (search page component's method override) - */ - async fetchAndUpdateItems() { - this.favoritesService - .search(this.queryText, this.filterQuery, this.sortQuery) - .pipe(take(1)) - .subscribe(result => { - this.items = new Promise(resolve => { - resolve(result.data && result.data.filter(item => !this.isMensaThing(item))); - }); - }); - } - - /** - * Helper function as 'typeof' is not accessible in HTML - * - * @param item TODO - */ - isMensaThing(item: SCThings): boolean { - return ( - this.hasCategories(item) && - ((item.categories as string[]).includes('canteen') || - (item.categories as string[]).includes('cafe') || - (item.categories as string[]).includes('student canteen') || - (item.categories as string[]).includes('restaurant')) - ); - } - - /** - * TODO - * - * @param item TODO - */ - hasCategories(item: SCThings): item is SCThings & {categories: string[]} { - return typeof (item as {categories: string[]}).categories !== 'undefined'; - } - - /** - * Emit event that an item was selected - */ - notifySelect(item: SCThings) { - this.dataRoutingService.emitChildEvent(item); - } + constructor(private favoritesService: FavoritesService) {} } diff --git a/src/app/modules/dashboard/sections/mensa-section/mensa-section-content.component.ts b/src/app/modules/dashboard/sections/mensa-section/mensa-section-content.component.ts index 4765f9a4..6dfcbcbc 100644 --- a/src/app/modules/dashboard/sections/mensa-section/mensa-section-content.component.ts +++ b/src/app/modules/dashboard/sections/mensa-section/mensa-section-content.component.ts @@ -12,11 +12,11 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ -import {Component, Input} from '@angular/core'; +import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; import {SCDish, SCPlace, SCThings} from '@openstapps/core'; import {PlaceMensaService} from '../../../data/types/place/special/mensa/place-mensa-service'; -import {animate, style, transition, trigger} from '@angular/animations'; import moment from 'moment'; +import {fadeAnimation} from '../../fade.animation'; /** * Shows a section with meals of the chosen mensa @@ -25,11 +25,8 @@ import moment from 'moment'; selector: 'stapps-mensa-section-content', templateUrl: 'mensa-section-content.component.html', styleUrls: ['mensa-section-content.component.scss'], - animations: [ - trigger('fade', [ - transition(':enter', [style({opacity: '0'}), animate('500ms ease', style({opacity: '1'}))]), - ]), - ], + animations: [fadeAnimation], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class MensaSectionContentComponent { /** diff --git a/src/app/modules/dashboard/sections/mensa-section/mensa-section.component.html b/src/app/modules/dashboard/sections/mensa-section/mensa-section.component.html index a8bfd224..cf55a5c5 100644 --- a/src/app/modules/dashboard/sections/mensa-section/mensa-section.component.html +++ b/src/app/modules/dashboard/sections/mensa-section/mensa-section.component.html @@ -30,7 +30,7 @@ {{ 'dashboard.canteens.no_favorite_prefix' | translate }} - {{ 'dashboard.canteens.no_favorite_link' | translate }} + {{ 'dashboard.canteens.no_favorite_link' | translate }} {{ 'dashboard.canteens.no_favorite_suffix' | translate }} diff --git a/src/app/modules/dashboard/sections/mensa-section/mensa-section.component.ts b/src/app/modules/dashboard/sections/mensa-section/mensa-section.component.ts index f8043538..ae4fecf6 100644 --- a/src/app/modules/dashboard/sections/mensa-section/mensa-section.component.ts +++ b/src/app/modules/dashboard/sections/mensa-section/mensa-section.component.ts @@ -12,23 +12,11 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ -import {Component} from '@angular/core'; -import {ActivatedRoute, Router} from '@angular/router'; -import {AlertController, AnimationController, ModalController} from '@ionic/angular'; -import {combineLatest, Subscription} from 'rxjs'; -import {debounceTime, distinctUntilChanged, startWith, take} from 'rxjs/operators'; -import {NGXLogger} from 'ngx-logger'; -import {SCThings} from '@openstapps/core'; - -import {DataProvider} from '../../../data/data.provider'; -import {DataRoutingService} from '../../../data/data-routing.service'; -import {FoodDataListComponent} from '../../../data/list/food-data-list.component'; -import {PositionService} from '../../../map/position.service'; -import {SettingsProvider} from '../../../settings/settings.provider'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; +import {map} from 'rxjs/operators'; import {FavoritesService} from '../../../favorites/favorites.service'; -import {ContextMenuService} from '../../../menu/context/context-menu.service'; -import {ConfigProvider} from '../../../config/config.provider'; -import {animate, style, transition, trigger} from '@angular/animations'; +import {fadeAnimation} from '../../fade.animation'; +import {isMensaThing} from '../../mensa-filters'; /** * Shows a section with meals of the chosen mensa @@ -37,107 +25,11 @@ import {animate, style, transition, trigger} from '@angular/animations'; selector: 'stapps-mensa-section', templateUrl: 'mensa-section.component.html', styleUrls: ['mensa-section.component.scss'], - animations: [ - trigger('fade', [transition(':enter', [style({opacity: '0'}), animate(250, style({opacity: '1'}))])]), - ], + animations: [fadeAnimation], + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class MensaSectionComponent extends FoodDataListComponent { - sub: Subscription; +export class MensaSectionComponent { + items = this.favoritesService.favoriteThings$.pipe(map(favorites => favorites.filter(isMensaThing))); - constructor( - protected readonly alertController: AlertController, - protected dataProvider: DataProvider, - protected readonly contextMenuService: ContextMenuService, - protected readonly settingsProvider: SettingsProvider, - protected readonly logger: NGXLogger, - protected dataRoutingService: DataRoutingService, - protected router: Router, - route: ActivatedRoute, - protected positionService: PositionService, - public modalController: ModalController, - protected favoritesService: FavoritesService, - configProvider: ConfigProvider, - animationController: AnimationController, - ) { - super( - alertController, - dataProvider, - contextMenuService, - settingsProvider, - logger, - dataRoutingService, - router, - route, - positionService, - configProvider, - animationController, - ); - } - - async initialize() { - super.initialize(); - - this.subscriptions.push( - combineLatest([ - this.queryTextChanged.pipe( - debounceTime(this.searchQueryDueTime), - distinctUntilChanged(), - startWith(this.queryText), - ), - this.favoritesService.favoritesChanged$, - ]).subscribe(async query => { - this.queryText = query[0]; - this.from = 0; - if (typeof this.filterQuery !== 'undefined' || this.queryText?.length > 0 || this.showDefaultData) { - await this.fetchAndUpdateItems(); - this.queryChanged.next(); - } - }), - ); - } - - /** - * Fetches/updates the favorites (search page component's method override) - */ - async fetchAndUpdateItems() { - this.favoritesService - .search(this.queryText, this.filterQuery, this.sortQuery) - .pipe(take(1)) - .subscribe(result => { - this.items = new Promise(resolve => { - resolve(result.data && result.data.filter(item => this.isMensaThing(item))); - }); - }); - } - - /** - * Helper function as 'typeof' is not accessible in HTML - * - * @param item TODO - */ - isMensaThing(item: SCThings): boolean { - return ( - this.hasCategories(item) && - ((item.categories as string[]).includes('canteen') || - (item.categories as string[]).includes('cafe') || - (item.categories as string[]).includes('student canteen') || - (item.categories as string[]).includes('restaurant')) - ); - } - - /** - * TODO - * - * @param item TODO - */ - hasCategories(item: SCThings): item is SCThings & {categories: string[]} { - return typeof (item as {categories: string[]}).categories !== 'undefined'; - } - - /** - * Action when user clicked edit to this section - */ - onSectionEdit() { - void this.router.navigate(['/canteen']); - } + constructor(protected favoritesService: FavoritesService) {} } diff --git a/src/app/modules/dashboard/sections/news-section/news-section.component.scss b/src/app/modules/dashboard/sections/news-section/news-section.component.scss index 0930dad6..64d62738 100644 --- a/src/app/modules/dashboard/sections/news-section/news-section.component.scss +++ b/src/app/modules/dashboard/sections/news-section/news-section.component.scss @@ -18,7 +18,6 @@ simple-swiper { } .more-news { - width: 128px; font-size: var(--font-size-xl); --color: var(--ion-color-medium-tint); diff --git a/src/app/modules/dashboard/sections/news-section/news-section.component.ts b/src/app/modules/dashboard/sections/news-section/news-section.component.ts index 98b5e223..ccbf57ca 100644 --- a/src/app/modules/dashboard/sections/news-section/news-section.component.ts +++ b/src/app/modules/dashboard/sections/news-section/news-section.component.ts @@ -12,11 +12,9 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ -import {Component} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; import {NewsProvider} from '../../../news/news.provider'; -import {SCMessage} from '@openstapps/core'; -import {animate, style, transition, trigger} from '@angular/animations'; -import {Router} from '@angular/router'; +import {fadeAnimation} from '../../fade.animation'; /** * Shows a section with news @@ -25,21 +23,14 @@ import {Router} from '@angular/router'; selector: 'stapps-news-section', templateUrl: 'news-section.component.html', styleUrls: ['news-section.component.scss'], - animations: [ - trigger('fade', [ - transition(':enter', [ - style({opacity: '0', transform: 'translateX(100px)'}), - animate('250ms ease', style({opacity: '1', transform: 'translateX(0)'})), - ]), - ]), - ], + animations: [fadeAnimation], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class NewsSectionComponent { - news: Promise; + news = this.newsProvider + .getCurrentFilters() + // eslint-disable-next-line unicorn/prefer-top-level-await,unicorn/consistent-function-scoping + .then(filters => this.newsProvider.getList(5, 0, filters)); - constructor(readonly newsProvider: NewsProvider, readonly router: Router) { - this.news = this.newsProvider - .getCurrentFilters() - .then(filters => this.newsProvider.getList(5, 0, filters)); - } + constructor(readonly newsProvider: NewsProvider) {} } diff --git a/src/app/modules/dashboard/sections/search-section/search-section.component.html b/src/app/modules/dashboard/sections/search-section/search-section.component.html index 6365aa5f..0acd2507 100644 --- a/src/app/modules/dashboard/sections/search-section/search-section.component.html +++ b/src/app/modules/dashboard/sections/search-section/search-section.component.html @@ -15,7 +15,7 @@ diff --git a/src/app/modules/dashboard/sections/search-section/search-section.component.ts b/src/app/modules/dashboard/sections/search-section/search-section.component.ts index 053241f7..a4c4783a 100644 --- a/src/app/modules/dashboard/sections/search-section/search-section.component.ts +++ b/src/app/modules/dashboard/sections/search-section/search-section.component.ts @@ -13,10 +13,7 @@ * this program. If not, see . */ import {Component} from '@angular/core'; -import {Router} from '@angular/router'; -import {Capacitor} from '@capacitor/core'; -import {Keyboard} from '@capacitor/keyboard'; -import {AnimationBuilder, AnimationController} from '@ionic/angular'; +import {AnimationController} from '@ionic/angular'; import {homePageSearchTransition} from './search-route-transition'; /** @@ -28,29 +25,7 @@ import {homePageSearchTransition} from './search-route-transition'; styleUrls: ['search-section.component.scss'], }) export class SearchSectionComponent { - searchTerm = ''; + routeTransition = homePageSearchTransition(this.animationController); - routeTransition: AnimationBuilder; - - constructor(private router: Router, private animationController: AnimationController) { - this.routeTransition = homePageSearchTransition(this.animationController); - } - - /** - * User submits search - */ - onSubmitSearch() { - this.router - .navigate(['/search'], {queryParams: {query: this.searchTerm}}) - .then(() => this.hideKeyboard()); - } - - /** - * Hides keyboard in native app environments - */ - hideKeyboard() { - if (Capacitor.isNativePlatform()) { - Keyboard.hide(); - } - } + constructor(private animationController: AnimationController) {} } diff --git a/src/app/modules/favorites/favorites.service.ts b/src/app/modules/favorites/favorites.service.ts index 24423907..b936e982 100644 --- a/src/app/modules/favorites/favorites.service.ts +++ b/src/app/modules/favorites/favorites.service.ts @@ -51,6 +51,8 @@ export class FavoritesService { // using debounce time 0 allows change detection to run through async suspension favoritesChanged$ = this.favorites.pipe(debounceTime(0)); + favoriteThings$ = this.favoritesChanged$.pipe(map(favorite => [...favorite.values()].map(it => it.data))); + static getDataFromFavorites(items: SCFavorite[]) { return items.map(item => item.data); } diff --git a/src/app/modules/profile/page/profile-page-section.html b/src/app/modules/profile/page/profile-page-section.html index 053ea254..ac78accb 100644 --- a/src/app/modules/profile/page/profile-page-section.html +++ b/src/app/modules/profile/page/profile-page-section.html @@ -28,6 +28,7 @@ *ngFor="let link of item.links" [routerLink]="link.link" [disabled]="link.needsAuth && !isLoggedIn" + [detail]="false" >
diff --git a/src/app/modules/profile/page/profile-page-section.scss b/src/app/modules/profile/page/profile-page-section.scss index fd2d18f1..f4169e7d 100644 --- a/src/app/modules/profile/page/profile-page-section.scss +++ b/src/app/modules/profile/page/profile-page-section.scss @@ -12,6 +12,8 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ +@use 'sass:math'; + $width: 108px; simple-swiper { @@ -22,7 +24,7 @@ simple-swiper { @each $i in 7, 6, 5, 4, 3, 2, 1 { $max: #{($width + 8px) * $i}; @container (inline-size < #{$max}) { - --swiper-slide-width: #{100cqi / $i}; + --swiper-slide-width: #{math.div(100cqi, $i)}; } } } diff --git a/src/app/util/ion-icon/icon.component.ts b/src/app/util/ion-icon/icon.component.ts index 4c5a1747..62a0fd02 100644 --- a/src/app/util/ion-icon/icon.component.ts +++ b/src/app/util/ion-icon/icon.component.ts @@ -1,24 +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. + * Copyright (C) 2023 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. + * 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 . + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ -import {Component, HostBinding, Input} from '@angular/core'; +import {ChangeDetectionStrategy, Component, HostBinding, Input} from '@angular/core'; @Component({ selector: 'stapps-icon', templateUrl: 'icon.html', styleUrls: ['icon.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class IconComponent { @HostBinding('style.--size') diff --git a/src/app/util/section.component.html b/src/app/util/section.component.html index 469f032b..18e62b92 100644 --- a/src/app/util/section.component.html +++ b/src/app/util/section.component.html @@ -25,14 +25,24 @@ - + - + - + diff --git a/src/app/util/section.component.scss b/src/app/util/section.component.scss index 7d31943e..982fc2b6 100644 --- a/src/app/util/section.component.scss +++ b/src/app/util/section.component.scss @@ -55,6 +55,7 @@ ion-col { } :host { + transition: height 250ms ease; display: block; padding: var(--spacing-sm) var(--spacing-md) var(--spacing-sm); --swiper-scroll-padding: var(--spacing-md); diff --git a/src/app/util/section.component.ts b/src/app/util/section.component.ts index b0504acc..ef228d27 100644 --- a/src/app/util/section.component.ts +++ b/src/app/util/section.component.ts @@ -12,8 +12,11 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ -import {AfterContentInit, Component, Input, OnDestroy, ViewContainerRef} from '@angular/core'; +import {AfterContentInit, ChangeDetectionStrategy, Component, Input, ViewContainerRef} from '@angular/core'; import {SCThings} from '@openstapps/core'; +import {fromMutationObserver} from '../_helpers/rxjs/mutation-observer'; +import {mergeMap, ReplaySubject, takeLast} from 'rxjs'; +import {distinctUntilChanged, filter, map, startWith} from 'rxjs/operators'; /** * Shows a horizontal list of action chips @@ -22,50 +25,34 @@ import {SCThings} from '@openstapps/core'; selector: 'stapps-section', templateUrl: 'section.component.html', styleUrls: ['section.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class SectionComponent implements AfterContentInit, OnDestroy { +export class SectionComponent implements AfterContentInit { @Input() title = ''; @Input() item?: SCThings; - mutationObserver: MutationObserver; + nativeElement = new ReplaySubject(1); - swiper?: HTMLElement; + swiper = this.nativeElement.pipe( + takeLast(1), + mergeMap(element => + fromMutationObserver(element, { + childList: true, + subtree: true, + }).pipe( + startWith([]), + map(() => element.querySelector('simple-swiper') as HTMLElement), + distinctUntilChanged(), + filter(element => !!element), + ), + ), + ); constructor(readonly viewContainerRef: ViewContainerRef) {} ngAfterContentInit() { - this.mutationObserver = new MutationObserver(() => { - const simpleSwiper = this.viewContainerRef.element.nativeElement.querySelector('simple-swiper'); - if (!simpleSwiper) return; - - this.swiper = simpleSwiper; - }); - this.mutationObserver.observe(this.viewContainerRef.element.nativeElement, { - childList: true, - subtree: true, - }); - } - - slideNext() { - if (this.swiper) { - this.swiper.scrollBy({ - left: this.swiper.offsetWidth, - behavior: 'smooth', - }); - } - } - - slidePrev() { - if (this.swiper) { - this.swiper.scrollBy({ - left: -this.swiper.offsetWidth, - behavior: 'smooth', - }); - } - } - - ngOnDestroy() { - this.mutationObserver.disconnect(); + this.nativeElement.next(this.viewContainerRef.element.nativeElement); + this.nativeElement.complete(); } } diff --git a/src/app/util/simple-swiper.component.ts b/src/app/util/simple-swiper.component.ts index 640cc1d5..def5906a 100644 --- a/src/app/util/simple-swiper.component.ts +++ b/src/app/util/simple-swiper.component.ts @@ -13,15 +13,12 @@ * this program. If not, see . */ -import {Component, ContentChildren, ElementRef, ViewContainerRef} from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; @Component({ selector: 'simple-swiper', templateUrl: 'simple-swiper.html', styleUrls: ['simple-swiper.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class SimpleSwiperComponent { - constructor(readonly viewContainerRef: ViewContainerRef) {} - - @ContentChildren('*') children: ElementRef; -} +export class SimpleSwiperComponent {} diff --git a/src/app/util/simple-swiper.scss b/src/app/util/simple-swiper.scss index 1768aba4..32a2ed16 100644 --- a/src/app/util/simple-swiper.scss +++ b/src/app/util/simple-swiper.scss @@ -29,7 +29,7 @@ gap: var(--swiper-gap, 0); - &::ng-deep > *:not(ion-button) { + &::ng-deep > * { contain: layout; scroll-snap-align: start; scroll-margin-inline: var(--swiper-scroll-padding, 0); diff --git a/src/app/util/util.module.ts b/src/app/util/util.module.ts index c003e269..b9d9300d 100644 --- a/src/app/util/util.module.ts +++ b/src/app/util/util.module.ts @@ -30,9 +30,10 @@ import {ThingTranslateModule} from '../translation/thing-translate.module'; import {SimpleSwiperComponent} from './simple-swiper.component'; import {SearchbarAutofocusDirective} from './searchbar-autofocus.directive'; import {SectionComponent} from './section.component'; +import {RouterModule} from '@angular/router'; @NgModule({ - imports: [BrowserModule, IonicModule, TranslateModule, ThingTranslateModule.forChild()], + imports: [BrowserModule, IonicModule, TranslateModule, ThingTranslateModule.forChild(), RouterModule], declarations: [ ElementSizeChangeDirective, ArrayLastPipe,