From d63f98553a0dc231ebb9b2244886cc5d104aa350 Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Tue, 16 Jul 2024 11:05:58 +0200 Subject: [PATCH 1/2] chore(notifications): 999 fix bug --- .../detail/notification-detail.component.ts | 10 ++++--- .../presentation/notifications.component.ts | 6 +++- .../shared/components/toasts/toast.service.ts | 2 +- .../notification-action-modal.component.ts | 6 ++-- .../notification-processing.service.ts | 29 +++++++++++++++++-- 5 files changed, 41 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/modules/page/notifications/detail/notification-detail.component.ts b/frontend/src/app/modules/page/notifications/detail/notification-detail.component.ts index b472adc1a3..4991011a03 100644 --- a/frontend/src/app/modules/page/notifications/detail/notification-detail.component.ts +++ b/frontend/src/app/modules/page/notifications/detail/notification-detail.component.ts @@ -92,11 +92,15 @@ export class NotificationDetailComponent implements AfterViewInit, OnDestroy { } else if (result?.error) { this.toastService.error(`requestNotification.failed${ formattedStatus }`, 15000, true); } - this.notificationProcessingService.notificationIdsInLoadingState.delete(result?.context?.notificationId); + this.notificationProcessingService.deleteNotificationId(result?.context?.notificationId); this.ngAfterViewInit(); }, }); + this.notificationProcessingService.doneEmit.subscribe(() => { + this.ngAfterViewInit(); + }); + this.selected$ = this.notificationDetailFacade.selected$; this.paramSubscription = this.route.queryParams.subscribe(params => { @@ -107,9 +111,7 @@ export class NotificationDetailComponent implements AfterViewInit, OnDestroy { } public ngAfterViewInit(): void { - if (!this.notificationDetailFacade.selected?.data) { - this.selectedNotificationBasedOnUrl(); - } + this.selectedNotificationBasedOnUrl(); this.subscription = this.selected$ .pipe( diff --git a/frontend/src/app/modules/page/notifications/presentation/notifications.component.ts b/frontend/src/app/modules/page/notifications/presentation/notifications.component.ts index 29619ea054..e4e722c4aa 100644 --- a/frontend/src/app/modules/page/notifications/presentation/notifications.component.ts +++ b/frontend/src/app/modules/page/notifications/presentation/notifications.component.ts @@ -96,7 +96,7 @@ export class NotificationsComponent { this.toastService.error(`requestNotification.failed${ formatted }`, 15000, true); } this.handleConfirmActionCompletedEvent(); - this.notificationProcessingService.notificationIdsInLoadingState.delete(result?.context?.notificationId); + this.notificationProcessingService.deleteNotificationId(result?.context?.notificationId); }, }); } @@ -111,6 +111,10 @@ export class NotificationsComponent { this.notificationsFacade.setReceivedNotifications(this.pagination.page, this.pagination.pageSize, this.notificationReceivedSortList, deeplinkNotificationFilter?.receivedFilter, this.receivedFilter); this.notificationsFacade.setQueuedAndRequestedNotifications(this.pagination.page, this.pagination.pageSize, this.notificationQueuedAndRequestedSortList, deeplinkNotificationFilter?.sentFilter, this.requestedFilter); }); + + this.notificationProcessingService.doneEmit.subscribe(() => { + this.ngOnInit(); + }); } public ngAfterViewInit(): void { diff --git a/frontend/src/app/modules/shared/components/toasts/toast.service.ts b/frontend/src/app/modules/shared/components/toasts/toast.service.ts index 600966c372..3d005e5146 100644 --- a/frontend/src/app/modules/shared/components/toasts/toast.service.ts +++ b/frontend/src/app/modules/shared/components/toasts/toast.service.ts @@ -62,7 +62,7 @@ export class ToastService { } public emitClick(): void { - this.notificationProcessingService.notificationIdsInLoadingState.add(this.apiService.lastRequest?.context?.notificationId); + this.notificationProcessingService.addNotificationId(this.apiService.lastRequest?.context?.notificationId); this.apiService.retryLastRequest()?.subscribe({ next: (next) => this.retryAction.emit({ success: next, context: this.apiService.lastRequest?.context }), error: (err) => this.retryAction.emit({ error: err, context: this.apiService.lastRequest?.context }), diff --git a/frontend/src/app/modules/shared/modules/notification/modal/actions/notification-action-modal.component.ts b/frontend/src/app/modules/shared/modules/notification/modal/actions/notification-action-modal.component.ts index c526b1b609..43c522f090 100644 --- a/frontend/src/app/modules/shared/modules/notification/modal/actions/notification-action-modal.component.ts +++ b/frontend/src/app/modules/shared/modules/notification/modal/actions/notification-action-modal.component.ts @@ -124,16 +124,16 @@ export class NotificationActionModalComponent { } const onConfirm = (isConfirmed: boolean) => { if (!isConfirmed) return; - this.notificationProcessingService.notificationIdsInLoadingState.add(notification.id); + this.notificationProcessingService.addNotificationId(notification.id); const reason = this.formGroup.get('reason').value; this.callback(desiredStatus, notification.id, reason).subscribe({ next: () => { - this.notificationProcessingService.notificationIdsInLoadingState.delete(notification.id); + this.notificationProcessingService.deleteNotificationId(notification.id); this.toastService.success(modalData.successMessage); this.confirmActionCompleted.emit(); }, error: () => { - this.notificationProcessingService.notificationIdsInLoadingState.delete(notification.id); + this.notificationProcessingService.deleteNotificationId(notification.id); this.toastService.error(modalData.errorMessage, 15000, true); this.confirmActionCompleted.emit(); }, diff --git a/frontend/src/app/modules/shared/service/notification-processing.service.ts b/frontend/src/app/modules/shared/service/notification-processing.service.ts index 5066c5ea8d..b7e7d278f4 100644 --- a/frontend/src/app/modules/shared/service/notification-processing.service.ts +++ b/frontend/src/app/modules/shared/service/notification-processing.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { EventEmitter, Injectable } from '@angular/core'; import { Notification } from '@shared/model/notification.model'; /******************************************************************************** @@ -23,9 +23,32 @@ import { Notification } from '@shared/model/notification.model'; providedIn: 'root', }) export class NotificationProcessingService { - notificationIdsInLoadingState: Set = new Set(); + private _notificationIdsInLoadingState: Set = new Set(); + doneEmit = new EventEmitter(); + + get notificationIdsInLoadingState(): Set { + return this._notificationIdsInLoadingState; + } + + set notificationIdsInLoadingState(ids: Set) { + this._notificationIdsInLoadingState = ids; + this.onNotificationIdsInLoadingStateRemoval(); + } + + public addNotificationId(id: string): void { + this._notificationIdsInLoadingState.add(id); + } + + public deleteNotificationId(id: string): void { + this._notificationIdsInLoadingState.delete(id); + this.onNotificationIdsInLoadingStateRemoval(); + } public isInLoadingProcess({ id } = {} as Notification): boolean { - return this.notificationIdsInLoadingState.has(id); + return this._notificationIdsInLoadingState.has(id); + } + + private onNotificationIdsInLoadingStateRemoval(): void { + this.doneEmit.emit(); } } From 7f7d53fb9b8700e487141234f72446073744f777 Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Tue, 16 Jul 2024 11:52:25 +0200 Subject: [PATCH 2/2] chore(notifications): 999 add tests --- .../notification-processing.service.spec.ts | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 frontend/src/app/modules/shared/service/notification-processing.service.spec.ts diff --git a/frontend/src/app/modules/shared/service/notification-processing.service.spec.ts b/frontend/src/app/modules/shared/service/notification-processing.service.spec.ts new file mode 100644 index 0000000000..2788439ba5 --- /dev/null +++ b/frontend/src/app/modules/shared/service/notification-processing.service.spec.ts @@ -0,0 +1,54 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +import { TestBed } from '@angular/core/testing'; +import { NotificationProcessingService } from '@shared/service/notification-processing.service'; + +describe('NotificationProcessingService', () => { + let service: NotificationProcessingService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [NotificationProcessingService], + }); + + service = TestBed.inject(NotificationProcessingService); + }); + + it('should set notificationIdsInLoadingState and call onNotificationIdsInLoadingStateRemoval', () => { + spyOn(service as any, 'onNotificationIdsInLoadingStateRemoval').and.callThrough(); + + const newIds = new Set(['id1', 'id2']); + + service.notificationIdsInLoadingState = newIds; + + expect(service.notificationIdsInLoadingState).toEqual(newIds); + expect((service as any).onNotificationIdsInLoadingStateRemoval).toHaveBeenCalled(); + }); + + it('should emit doneEmit when notificationIdsInLoadingState is set', () => { + spyOn(service.doneEmit, 'emit'); + + const newIds = new Set(['id1', 'id2']); + + service.notificationIdsInLoadingState = newIds; + + expect(service.doneEmit.emit).toHaveBeenCalled(); + }); +}); +