From 58c64410e3f05347f6dfadd9d1bab3429f7f1723 Mon Sep 17 00:00:00 2001 From: Andrei Yurkouski Date: Fri, 3 Oct 2025 17:39:31 +0200 Subject: [PATCH] feat: show error message in the bottom sheet --- .../outlet/private-outlet/private-outlet.ts | 33 +++++++++++++++++-- src/app/pages/article-page/article-page.ts | 18 ++++++++-- src/app/pages/articles-page/articles-page.ts | 15 +++++++-- .../pages/bookmarks-page/bookmarks-page.ts | 5 +++ src/app/pages/feeds-page/feeds-page.ts | 9 +++-- src/app/services/notifications-service.ts | 21 ++++++++++++ 6 files changed, 93 insertions(+), 8 deletions(-) create mode 100644 src/app/services/notifications-service.ts diff --git a/src/app/outlet/private-outlet/private-outlet.ts b/src/app/outlet/private-outlet/private-outlet.ts index 7c48e3f..743a82f 100644 --- a/src/app/outlet/private-outlet/private-outlet.ts +++ b/src/app/outlet/private-outlet/private-outlet.ts @@ -1,6 +1,11 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core' +import { ChangeDetectionStrategy, Component, DestroyRef, inject, OnInit } from '@angular/core' import { NavComponent } from '../../components/nav/nav.component' import { RouterOutlet } from '@angular/router' +import { NotificationsService } from '../../services/notifications-service' +import { catchError, of, tap } from 'rxjs' +import { MatBottomSheet } from '@angular/material/bottom-sheet' +import { BottomErrorSheet } from '../../components/bottom-error-sheet/bottom-error-sheet' +import { takeUntilDestroyed } from '@angular/core/rxjs-interop' @Component({ selector: 'app-private-outlet', @@ -9,4 +14,28 @@ import { RouterOutlet } from '@angular/router' styleUrl: './private-outlet.css', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class PrivateOutlet {} +export class PrivateOutlet implements OnInit { + private readonly notificationsService = inject(NotificationsService) + private readonly notification = this.notificationsService.$notification + private readonly errorSheet = inject(MatBottomSheet) + private readonly destroyRef = inject(DestroyRef) + + ngOnInit() { + this.notification + .pipe( + tap((n) => { + if (n === null) { + this.errorSheet.dismiss() + } else { + this.errorSheet.open(BottomErrorSheet, { data: { error: n?.message } }) + } + }), + takeUntilDestroyed(this.destroyRef), + catchError((error) => { + console.error(error) + return of(null) + }), + ) + .subscribe() + } +} diff --git a/src/app/pages/article-page/article-page.ts b/src/app/pages/article-page/article-page.ts index 77f4b71..05d40d9 100644 --- a/src/app/pages/article-page/article-page.ts +++ b/src/app/pages/article-page/article-page.ts @@ -23,6 +23,7 @@ import { HttpErrorResponse } from '@angular/common/http' import { TagService } from '../../services/tag-service' import { TitleService } from '../../services/title-service' import { DomSanitizer, SafeHtml } from '@angular/platform-browser' +import { NotificationsService } from '../../services/notifications-service' @Component({ selector: 'app-article-page', @@ -47,6 +48,7 @@ export class ArticlePage implements OnInit { private readonly titleService = inject(TitleService) private readonly destroyRef = inject(DestroyRef) private readonly domSanitizer = inject(DomSanitizer) + private readonly notificationsService = inject(NotificationsService) readonly article = signal
(null) readonly fullText = signal(undefined) @@ -86,6 +88,9 @@ export class ArticlePage implements OnInit { takeUntilDestroyed(this.destroyRef), catchError((error: HttpErrorResponse) => { console.error(error) + this.notificationsService.setNotification({ + message: error.error.message, + }) return of(null) }), ) @@ -117,6 +122,9 @@ export class ArticlePage implements OnInit { takeUntilDestroyed(this.destroyRef), catchError((error: HttpErrorResponse) => { console.error(error) + this.notificationsService.setNotification({ + message: error.error.message, + }) return of(null) }), ) @@ -147,6 +155,9 @@ export class ArticlePage implements OnInit { takeUntilDestroyed(this.destroyRef), catchError((error: HttpErrorResponse) => { console.error(error) + this.notificationsService.setNotification({ + message: error.error.message, + }) return of(null) }), ) @@ -171,9 +182,12 @@ export class ArticlePage implements OnInit { .getFullText({ articleId }) .pipe( takeUntilDestroyed(this.destroyRef), - catchError((e) => { - console.error(e) + catchError((error) => { + console.error(error) this.isLoading.set(false) + this.notificationsService.setNotification({ + message: error.error.message, + }) return of(null) }), ) diff --git a/src/app/pages/articles-page/articles-page.ts b/src/app/pages/articles-page/articles-page.ts index 1b8e97a..a51ecac 100644 --- a/src/app/pages/articles-page/articles-page.ts +++ b/src/app/pages/articles-page/articles-page.ts @@ -21,6 +21,7 @@ import { PageService } from '../../services/page-service' import { PageDisplayToggle } from '../../components/page-display-toggle/page-display-toggle' import { AsyncPipe } from '@angular/common' import { SortOrder } from '../../entities/base/base.enums' +import { NotificationsService } from '../../services/notifications-service' @Component({ selector: 'app-articles-page', @@ -48,6 +49,7 @@ export class ArticlesPage implements OnInit { private readonly tagService = inject(TagService) private readonly titleService = inject(TitleService) private readonly pageService = inject(PageService) + private readonly notificationsService = inject(NotificationsService) readonly articles = signal([]) readonly articleIds = computed(() => this.articles().map(({ _id }) => _id)) @@ -68,6 +70,9 @@ export class ArticlesPage implements OnInit { takeUntilDestroyed(this.destroyRef), catchError((error: HttpErrorResponse) => { console.log(error) + this.notificationsService.setNotification({ + message: error.error.message, + }) return of(null) }), ) @@ -203,6 +208,9 @@ export class ArticlesPage implements OnInit { takeUntilDestroyed(this.destroyRef), catchError((error: HttpErrorResponse) => { console.log(error) + this.notificationsService.setNotification({ + message: error.error.message, + }) return of(null) }), ) @@ -236,9 +244,12 @@ export class ArticlesPage implements OnInit { .refreshAllFeeds() .pipe( takeUntilDestroyed(this.destroyRef), - catchError((e) => { + catchError((error) => { this.isRefreshingAll.set(false) - console.error(e) + console.error(error) + this.notificationsService.setNotification({ + message: error.error.message, + }) return of(null) }), ) diff --git a/src/app/pages/bookmarks-page/bookmarks-page.ts b/src/app/pages/bookmarks-page/bookmarks-page.ts index 07c9f9f..993196d 100644 --- a/src/app/pages/bookmarks-page/bookmarks-page.ts +++ b/src/app/pages/bookmarks-page/bookmarks-page.ts @@ -12,6 +12,7 @@ import { MatToolbarRow } from '@angular/material/toolbar' import { Paginator } from '../../components/paginator/paginator' import { PageService } from '../../services/page-service' import { PageDisplayToggle } from '../../components/page-display-toggle/page-display-toggle' +import { NotificationsService } from '../../services/notifications-service' @Component({ selector: 'app-bookmarks-page', @@ -25,6 +26,7 @@ export class BookmarksPage implements OnInit { private readonly tagService = inject(TagService) private readonly pageService = inject(PageService) private readonly titleService = inject(TitleService) + private readonly notificationsService = inject(NotificationsService) articles = signal([]) @@ -73,6 +75,9 @@ export class BookmarksPage implements OnInit { takeUntilDestroyed(this.destroyRef), catchError((error: HttpErrorResponse) => { console.log(error) + this.notificationsService.setNotification({ + message: error.error.message, + }) return of(null) }), ) diff --git a/src/app/pages/feeds-page/feeds-page.ts b/src/app/pages/feeds-page/feeds-page.ts index 7606309..9b70f5e 100644 --- a/src/app/pages/feeds-page/feeds-page.ts +++ b/src/app/pages/feeds-page/feeds-page.ts @@ -24,6 +24,7 @@ import { FeedAddForm } from '../../components/feed-add-form/feed-add-form' import { FeedEditForm } from '../../components/feed-edit-form/feed-edit-form' import { MatBottomSheet } from '@angular/material/bottom-sheet' import { BottomErrorSheet } from '../../components/bottom-error-sheet/bottom-error-sheet' +import { NotificationsService } from '../../services/notifications-service' @Component({ selector: 'app-feed-page', @@ -57,6 +58,7 @@ export class FeedsPage implements OnInit { private readonly destroyRef = inject(DestroyRef) private readonly titleService = inject(TitleService) private readonly bottomError = inject(MatBottomSheet) + private readonly notificationsService = inject(NotificationsService) readonly feeds = signal([]) readonly isRefreshing = signal>({}) @@ -128,9 +130,12 @@ export class FeedsPage implements OnInit { .refreshAllFeeds() .pipe( takeUntilDestroyed(this.destroyRef), - catchError((e) => { + catchError((error) => { this.isRefreshingAll.set(false) - console.error(e) + console.error(error) + this.notificationsService.setNotification({ + message: error.error.message, + }) return of(null) }), ) diff --git a/src/app/services/notifications-service.ts b/src/app/services/notifications-service.ts new file mode 100644 index 0000000..6a8bf37 --- /dev/null +++ b/src/app/services/notifications-service.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@angular/core' +import { BehaviorSubject } from 'rxjs' + +type AppNotification = { + message: string +} + +@Injectable({ + providedIn: 'root', +}) +export class NotificationsService { + private $$notification = new BehaviorSubject(null) + $notification = this.$$notification.asObservable() + + setNotification(notification: AppNotification) { + this.$$notification.next(notification) + setTimeout(() => { + this.$$notification.next(null) + }, 3000) + } +}