From 37742556a0c9fe940344bea0bf470f5c2516ca10 Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Wed, 18 Aug 2021 15:01:15 +0200 Subject: [PATCH 01/17] Make subject row in split screen sticky --- .../routing/wp-split-view/wp-split-view.html | 5 +++- .../layout/work_packages/_details_view.sass | 27 ++++++++++++------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html index 610a72f625..d809a96feb 100644 --- a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html +++ b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html @@ -8,7 +8,10 @@ - +
diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-notifications-count.function.ts b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-notifications-count.function.ts index 34bfd313a9..760cb61a80 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-notifications-count.function.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-notifications-count.function.ts @@ -4,26 +4,16 @@ import { map } from 'rxjs/operators'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; import { NOTIFICATIONS_MAX_SIZE } from 'core-app/features/in-app-notifications/store/in-app-notification.model'; +import { InAppNotificationsService } from 'core-app/features/in-app-notifications/store/in-app-notifications.service'; export function workPackageNotificationsCount( workPackage:WorkPackageResource, injector:Injector, ):Observable { - const apiV3Service = injector.get(APIV3Service); - const wpId = workPackage.id?.toString() || ''; + const ianService = injector.get(InAppNotificationsService); - return apiV3Service - .notifications - .facet( - 'unread', - { - pageSize: NOTIFICATIONS_MAX_SIZE, - filters: [ - ['resourceId', '=', [wpId]], - ['resourceType', '=', ['WorkPackage']], - ], - }, - ) + return ianService + .notificationsOfWpLoaded .pipe( map((data) => data._embedded.elements.length), ); diff --git a/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts b/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts index 92b7a67da0..88d0f8bd31 100644 --- a/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts @@ -93,7 +93,6 @@ export class WorkPackagesFullViewComponent extends WorkPackageSingleViewBase imp this.isWatched = wp.hasOwnProperty('unwatch'); this.displayWatchButton = wp.hasOwnProperty('unwatch') || wp.hasOwnProperty('watch'); - // Todo check for User logged in this .ianService .notificationsOfWpLoaded diff --git a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts index c2cb41a24f..aded989e16 100644 --- a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts @@ -120,7 +120,6 @@ export class WorkPackageSplitViewComponent extends WorkPackageSingleViewBase imp } showNotificationsButton():boolean { - // Todo check for User logged in return this.displayNotificationsButton && this.keepTab.currentTabIdentifier === 'activity'; } } From 459972b5d2867896c8f3c2d2d946c4dcc2c0ce52 Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Mon, 23 Aug 2021 12:47:27 +0200 Subject: [PATCH 07/17] * Add test cases for "Mark as read" button on split- and full view. * Allow scrolling for split create form --- ...kage-mark-notification-button.component.ts | 4 +- .../routing/wp-full-view/wp-full-view.html | 1 + .../wp-split-view/wp-split-view.component.ts | 6 +- .../routing/wp-split-view/wp-split-view.html | 1 + .../work_packages/new/_split_view.sass | 2 + .../tabs/activity_notifications_spec.rb | 61 +++++++++++++++---- 6 files changed, 59 insertions(+), 16 deletions(-) diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-mark-notification-button/work-package-mark-notification-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-mark-notification-button/work-package-mark-notification-button.component.ts index de1631841a..0fc61f64c3 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-mark-notification-button/work-package-mark-notification-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-mark-notification-button/work-package-mark-notification-button.component.ts @@ -1,4 +1,6 @@ -import { Component, ChangeDetectionStrategy, Input, OnInit } from '@angular/core'; +import { + Component, ChangeDetectionStrategy, Input, OnInit, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { InAppNotificationsService } from 'core-app/features/in-app-notifications/store/in-app-notifications.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; diff --git a/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.html b/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.html index 9697077b46..8394796944 100644 --- a/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.html +++ b/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.html @@ -34,6 +34,7 @@ diff --git a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts index aded989e16..278e1a9474 100644 --- a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts @@ -26,7 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectionStrategy, Component, Injector, OnInit } from '@angular/core'; +import { + ChangeDetectionStrategy, Component, Injector, OnInit, +} from '@angular/core'; import { StateService } from '@uirouter/core'; import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service'; import { States } from 'core-app/core/states/states.service'; @@ -107,7 +109,7 @@ export class WorkPackageSplitViewComponent extends WorkPackageSingleViewBase imp }); } - get shouldFocus() { + get shouldFocus():boolean { return this.$state.params.focus === true; } diff --git a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html index 87e948f84f..cfbe403432 100644 --- a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html +++ b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html @@ -14,6 +14,7 @@ class="work-packages--details-button" [workPackage]="workPackage" buttonClasses="-round" + data-qa-selector="mark-notification-read-button" > diff --git a/frontend/src/global_styles/content/work_packages/new/_split_view.sass b/frontend/src/global_styles/content/work_packages/new/_split_view.sass index fb725d8e76..4970cdb055 100644 --- a/frontend/src/global_styles/content/work_packages/new/_split_view.sass +++ b/frontend/src/global_styles/content/work_packages/new/_split_view.sass @@ -2,6 +2,8 @@ // Override the top header in create mode &.-create-mode top: 0 + overflow-y: auto + // Details header containing switch icon and status|type row .work-packages--new-details-header margin: 0.375em 0 diff --git a/spec/features/work_packages/tabs/activity_notifications_spec.rb b/spec/features/work_packages/tabs/activity_notifications_spec.rb index a7f8083bb1..c31b29ddd2 100644 --- a/spec/features/work_packages/tabs/activity_notifications_spec.rb +++ b/spec/features/work_packages/tabs/activity_notifications_spec.rb @@ -20,14 +20,8 @@ describe 'Activity tab notifications', js: true, selenium: true do work_package end shared_let(:admin) { FactoryBot.create(:admin) } - shared_let(:full_view) { Pages::FullWorkPackage.new(work_package, project) } - before do - login_as(admin) - full_view.visit! - end - - context 'when there are notifications for the work package' do + shared_examples_for 'when there are notifications for the work package' do shared_let(:notification) do FactoryBot.create :notification, recipient: admin, @@ -35,24 +29,65 @@ describe 'Activity tab notifications', js: true, selenium: true do resource: work_package, journal: work_package.journals.last end - - it 'Shows a notification bubble with the right number' do + it 'shows a notification bubble with the right number' do expect(page).to have_selector('[data-qa-selector="tab-counter-Activity"]', text: '1') end - it 'Shows a notification icon next to activities that have an unread notification' do + it 'shows a notification icon next to activities that have an unread notification' do expect(page).to have_selector('[data-qa-selector="user-activity-bubble"]', count: 1) expect(page).to have_selector('[data-qa-activity-number="3"] [data-qa-selector="user-activity-bubble"]') end + + it 'shows a button to mark the notifications as read' do + expect(page).to have_selector('[data-qa-selector="mark-notification-read-button"]') + + # A click marks the notification as read ... + page.find('[data-qa-selector="mark-notification-read-button"]').click + + # ... and updates the view accordingly + expect(page).not_to have_selector('[data-qa-selector="mark-notification-read-button"]') + expect(page).not_to have_selector('[data-qa-selector="tab-counter-Activity"]') + expect(page).not_to have_selector('[data-qa-selector="user-activity-bubble"]') + end end - context 'when there are no notifications for the work package' do - it 'Shows no notification bubble' do + shared_examples_for 'when there are no notifications for the work package' do + it 'shows no notification bubble' do expect(page).not_to have_selector('[data-qa-selector="tab-counter-Activity"]') end - it 'Does not show any notification icons next to activities' do + it 'does not show any notification icons next to activities' do expect(page).not_to have_selector('[data-qa-selector="user-activity-bubble"]') end + + it 'shows no button to mark the notifications as read' do + expect(page).not_to have_selector('[data-qa-selector="mark-notification-read-button"]') + end + end + + context 'when on full view' do + shared_let(:full_view) { Pages::FullWorkPackage.new(work_package, project) } + + before do + login_as(admin) + full_view.visit_tab! 'activity' + end + + it_behaves_like 'when there are notifications for the work package' + + it_behaves_like 'when there are no notifications for the work package' + end + + context 'when on split view' do + shared_let(:split_view) { Pages::SplitWorkPackage.new(work_package, project) } + + before do + login_as(admin) + split_view.visit_tab! 'activity' + end + + it_behaves_like 'when there are notifications for the work package' + + it_behaves_like 'when there are no notifications for the work package' end end From f582824e68e8c26d9fb007f852360bcfb543a374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Tue, 24 Aug 2021 13:08:46 +0200 Subject: [PATCH 08/17] WIP: Implement scoped stores for notification state --- .../in-app-notification-bell.component.ts | 8 +- .../in-app-notification-center.component.html | 6 +- .../in-app-notification-center.component.ts | 26 +++-- .../store/in-app-notifications.query.ts | 9 +- .../store/in-app-notifications.service.ts | 105 +++++++----------- .../store/in-app-notifications.store.ts | 9 +- .../activity-base.controller.ts | 10 +- .../wp-full-view/wp-full-view.component.ts | 8 +- .../wp-split-view/wp-split-view.component.ts | 6 + 9 files changed, 98 insertions(+), 89 deletions(-) diff --git a/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts b/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts index 434bce9255..59ca53ab78 100644 --- a/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts +++ b/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts @@ -1,5 +1,6 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import { InAppNotificationsQuery } from 'core-app/features/in-app-notifications/store/in-app-notifications.query'; +import { InAppNotificationsStore } from 'core-app/features/in-app-notifications/store/in-app-notifications.store'; import { InAppNotificationsService } from 'core-app/features/in-app-notifications/store/in-app-notifications.service'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; import { merge, timer } from 'rxjs'; @@ -15,12 +16,17 @@ const POLLING_INTERVAL = 10000; templateUrl: './in-app-notification-bell.component.html', styleUrls: ['./in-app-notification-bell.component.sass'], changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + InAppNotificationsService, + InAppNotificationsStore, + InAppNotificationsQuery, + ], }) export class InAppNotificationBellComponent { polling$ = timer(10, POLLING_INTERVAL) .pipe( filter(() => this.activeWindow.isActive), - switchMap(() => this.inAppService.count$()), + switchMap(() => this.inAppService.fetchCount()), ); unreadCount$ = merge( diff --git a/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.html b/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.html index a2d0f115b0..abe43f402a 100644 --- a/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.html +++ b/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.html @@ -26,9 +26,9 @@ diff --git a/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts b/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts index db1cb1f8f1..99ab65ed7f 100644 --- a/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts +++ b/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts @@ -7,6 +7,7 @@ import { } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { InAppNotificationsQuery } from 'core-app/features/in-app-notifications/store/in-app-notifications.query'; +import { InAppNotificationsStore } from 'core-app/features/in-app-notifications/store/in-app-notifications.store'; import { InAppNotificationsService } from 'core-app/features/in-app-notifications/store/in-app-notifications.service'; import { NOTIFICATIONS_MAX_SIZE } from 'core-app/features/in-app-notifications/store/in-app-notification.model'; import { map } from 'rxjs/operators'; @@ -19,6 +20,11 @@ import { UIRouterGlobals } from '@uirouter/core'; templateUrl: './in-app-notification-center.component.html', styleUrls: ['./in-app-notification-center.component.sass'], changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + InAppNotificationsService, + InAppNotificationsStore, + InAppNotificationsQuery, + ], }) export class InAppNotificationCenterComponent implements OnInit { activeFacet$ = this.ianQuery.activeFacet$; @@ -42,6 +48,11 @@ export class InAppNotificationCenterComponent implements OnInit { map((facet:'unread'|'all') => this.text.no_results[facet] || this.text.no_results.unread), ); + totalCountWarning$ = this.ianQuery.notLoaded$.pipe(map((notLoaded:number) => this.I18n.t( + 'js.notifications.center.total_count_warning', + { newest_count: NOTIFICATIONS_MAX_SIZE, more_count: notLoaded }, + ))); + maxSize = NOTIFICATIONS_MAX_SIZE; facets:string[] = ['unread', 'all']; @@ -65,20 +76,11 @@ export class InAppNotificationCenterComponent implements OnInit { readonly ianQuery:InAppNotificationsQuery, readonly uiRouterGlobals:UIRouterGlobals, readonly state:StateService, - ) { - } + ) { } ngOnInit():void { - this.ianService.get(); - } - - totalCountWarning():string { - const state = this.ianQuery.getValue(); - - return this.I18n.t( - 'js.notifications.center.total_count_warning', - { newest_count: NOTIFICATIONS_MAX_SIZE, more_count: state.notShowing }, - ); + this.ianService.setActiveFacet('unread'); + this.ianService.setActiveFilters([]); } openSplitView($event:WorkPackageResource):void { diff --git a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.query.ts b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.query.ts index 1d94ac55d1..adaf78acc6 100644 --- a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.query.ts +++ b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.query.ts @@ -8,11 +8,16 @@ import { } from './in-app-notifications.store'; import { InAppNotification } from 'core-app/features/in-app-notifications/store/in-app-notification.model'; -@Injectable({ providedIn: 'root' }) +@Injectable() export class InAppNotificationsQuery extends QueryEntity { /** Select the active filter facet */ activeFacet$ = this.select('activeFacet'); + activeFetchParameters$ = this.select(['activeFacet', 'activeFilters']); + + /** Select the active filter facet */ + notLoaded$ = this.select('notLoaded'); + /** Get the faceted items */ faceted$ = this.activeFacet$ .pipe( @@ -60,7 +65,7 @@ export class InAppNotificationsQuery extends QueryEntity notShowing > 0), + map(({ notLoaded }) => notLoaded > 0), ); constructor(protected store:InAppNotificationsStore) { diff --git a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts index 7d042087fe..3220ed6797 100644 --- a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts +++ b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts @@ -1,17 +1,15 @@ -import { EventEmitter, Injectable } from '@angular/core'; +import { Injectable } from '@angular/core'; +import { map, switchMap, tap, take, debounceTime } from 'rxjs/operators'; import { applyTransaction, ID, setLoading } from '@datorama/akita'; -import { Observable } from 'rxjs'; +import { ApiV3ListFilter } from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; -import { map, switchMap, tap } from 'rxjs/operators'; import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; import { InAppNotificationsQuery } from 'core-app/features/in-app-notifications/store/in-app-notifications.query'; -import { take } from 'rxjs/internal/operators/take'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { InAppNotificationsStore } from './in-app-notifications.store'; import { InAppNotification, NOTIFICATIONS_MAX_SIZE } from './in-app-notification.model'; -import { IHALCollection } from 'core-app/core/apiv3/types/hal-collection.type'; -@Injectable({ providedIn: 'root' }) +@Injectable() export class InAppNotificationsService { constructor( private store:InAppNotificationsStore, @@ -19,43 +17,50 @@ export class InAppNotificationsService { private apiV3Service:APIV3Service, private notifications:NotificationsService, ) { + this.query.activeFetchParameters$ + .pipe(debounceTime(0)) + .subscribe(() => { + this.fetchNotifications(); + this.fetchCount(); + }); } - public notificationsOfWpLoaded:EventEmitter> = new EventEmitter>(); - - get():void { + fetchNotifications() { this.store.setLoading(true); - const facet = this.query.getValue().activeFacet; + const { activeFacet, activeFilters } = this.query.getValue(); - this + const call = this .apiV3Service .notifications - .facet(facet, { pageSize: NOTIFICATIONS_MAX_SIZE }) + .facet(activeFacet, { + pageSize: NOTIFICATIONS_MAX_SIZE, + filters: activeFilters, + }); + + call .pipe( tap((events) => this.sideLoadInvolvedWorkPackages(events._embedded.elements)), ) .subscribe( - (events) => { - applyTransaction(() => { - this.store.set(events._embedded.elements); - this.store.update({ notShowing: events.total - events.count }); - }); - }, - (error) => { - this.notifications.addError(error); - }, + (events) => applyTransaction(() => { + this.store.set(events._embedded.elements); + this.store.update({ notShowing: events.total - events.count }); + }), + (error) => this.notifications.addError(error), ) - .add( - () => this.store.setLoading(false), - ); + .add(() => this.store.setLoading(false)); + + return call; } - count$():Observable { + fetchCount() { + const { activeFilters } = this.query.getValue(); + return this .apiV3Service .notifications - .unread({ pageSize: 0 }) + .unread({ pageSize: 0, filters: activeFilters }) .pipe( map((events) => events.total), tap((unreadCount) => { @@ -72,8 +77,12 @@ export class InAppNotificationsService { this.store.update((state) => ({ ...state, activeFacet: facet })); } - markAllRead():void { - this.query + setActiveFilters(filters:ApiV3ListFilter[]):void { + this.store.update((state) => ({ ...state, activeFilters: filters })); + } + + markAllRead() { + return this.query .unread$ .pipe( take(1), @@ -88,10 +97,10 @@ export class InAppNotificationsService { }); } - markAsRead(notifications:InAppNotification[], keep = false):void { + markAsRead(notifications:InAppNotification[], keep = false) { const ids = notifications.map((n) => n.id); - this + return this .apiV3Service .notifications .markRead(ids) @@ -108,24 +117,6 @@ export class InAppNotificationsService { }); } - loadNotificationsOfWorkPackage(workPackageId:string):void { - this - .apiV3Service - .notifications - .facet( - 'unread', - { - pageSize: NOTIFICATIONS_MAX_SIZE, - filters: [ - ['resourceId', '=', [workPackageId]], - ['resourceType', '=', ['WorkPackage']], - ], - }, - ).subscribe((notificationCollection) => { - this.notificationsOfWpLoaded.emit(notificationCollection); - }); - } - private sideLoadInvolvedWorkPackages(elements:InAppNotification[]) { const wpIds = elements.map((element) => { const href = element._links.resource?.href; @@ -137,22 +128,4 @@ export class InAppNotificationsService { .work_packages .requireAll(_.compact(wpIds)); } - - collapse(notification:InAppNotification):void { - this.store.update( - notification.id, - { - expanded: false, - }, - ); - } - - expand(notification:InAppNotification):void { - this.store.update( - notification.id, - { - expanded: true, - }, - ); - } } diff --git a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.store.ts b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.store.ts index c53b8280da..5a415c207c 100644 --- a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.store.ts +++ b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.store.ts @@ -1,26 +1,29 @@ import { Injectable } from '@angular/core'; import { EntityState, EntityStore, StoreConfig } from '@datorama/akita'; import { InAppNotification } from './in-app-notification.model'; +import { ApiV3ListFilter } from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; export interface InAppNotificationsState extends EntityState { /** The entities in the store might not all be unread so we keep separate count */ unreadCount:number; /** Number of elements not showing after max values loaded */ - notShowing:number; + notLoaded:number; activeFacet:string; + activeFilters:ApiV3ListFilter[]; expanded:boolean; } export function createInitialState():InAppNotificationsState { return { unreadCount: 0, - notShowing: 0, + notLoaded: 0, activeFacet: 'unread', + activeFilters: [], expanded: false, }; } -@Injectable({ providedIn: 'root' }) +@Injectable() @StoreConfig({ name: 'in-app-notifications' }) export class InAppNotificationsStore extends EntityStore { constructor() { diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts index 1e88addfb9..b4ad7ab6f8 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts @@ -28,7 +28,6 @@ import { ChangeDetectorRef, Directive, OnInit } from '@angular/core'; import { Transition } from '@uirouter/core'; -import { combineLatest } from 'rxjs'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { ActivityEntryInfo } from 'core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-entry-info'; @@ -95,6 +94,15 @@ export class ActivityPanelBaseController extends UntilDestroyedMixin implements }); }); + + this.ianService.setActiveFacet('unread'); + this.ianService.setActiveFilters([ + ['resourceId', '=', [workPackageId]], + ['resourceType', '=', ['WorkPackage']], + ]); + this.ianService.fetchNotifications(); + + this.ianService.loadNotificationsOfWorkPackage(this.workPackageId); this.ianService.notificationsOfWpLoaded.subscribe((notificationCollection) => { this.notifications = notificationCollection._embedded.elements; diff --git a/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts b/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts index 88d0f8bd31..e548fa6e23 100644 --- a/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts @@ -29,9 +29,12 @@ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { StateService } from '@uirouter/core'; import { Component, Injector, OnInit } from '@angular/core'; +import { of } from 'rxjs'; import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; import { WorkPackageSingleViewBase } from 'core-app/features/work-packages/routing/wp-view-base/work-package-single-view.base'; -import { of } from 'rxjs'; +import { InAppNotificationsQuery } from 'core-app/features/in-app-notifications/store/in-app-notifications.query'; +import { InAppNotificationsStore } from 'core-app/features/in-app-notifications/store/in-app-notifications.store'; +import { InAppNotificationsService } from 'core-app/features/in-app-notifications/store/in-app-notifications.service'; import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; import { InAppNotificationsService } from 'core-app/features/in-app-notifications/store/in-app-notifications.service'; @@ -43,6 +46,9 @@ import { InAppNotificationsService } from 'core-app/features/in-app-notification host: { class: 'work-packages-page--ui-view' }, providers: [ { provide: HalResourceNotificationService, useExisting: WorkPackageNotificationService }, + InAppNotificationsService, + InAppNotificationsStore, + InAppNotificationsQuery, ], }) export class WorkPackagesFullViewComponent extends WorkPackageSingleViewBase implements OnInit { diff --git a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts index aded989e16..d8b7de7695 100644 --- a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts @@ -30,6 +30,9 @@ import { ChangeDetectionStrategy, Component, Injector, OnInit } from '@angular/c import { StateService } from '@uirouter/core'; import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service'; import { States } from 'core-app/core/states/states.service'; +import { InAppNotificationsQuery } from 'core-app/features/in-app-notifications/store/in-app-notifications.query'; +import { InAppNotificationsStore } from 'core-app/features/in-app-notifications/store/in-app-notifications.store'; +import { InAppNotificationsService } from 'core-app/features/in-app-notifications/store/in-app-notifications.service'; import { FirstRouteService } from 'core-app/core/routing/first-route-service'; import { KeepTabService } from 'core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service'; import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; @@ -45,6 +48,9 @@ import { InAppNotificationsService } from 'core-app/features/in-app-notification selector: 'wp-split-view-entry', providers: [ { provide: HalResourceNotificationService, useClass: WorkPackageNotificationService }, + InAppNotificationsService, + InAppNotificationsStore, + InAppNotificationsQuery, ], }) export class WorkPackageSplitViewComponent extends WorkPackageSingleViewBase implements OnInit { From c5caf237a67249e309dc118af0f959e061789533 Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Tue, 24 Aug 2021 15:03:26 +0200 Subject: [PATCH 09/17] Use new service methods to load notification data correctly (wip) --- .../in-app-notification-center.component.ts | 23 +++++++++++------- .../facet/activate-facet-button.component.ts | 1 - .../store/in-app-notifications.service.ts | 2 +- ...kage-mark-notification-button.component.ts | 18 +++----------- .../activity-base.controller.ts | 23 +++++++++--------- .../activity-panel/activity-tab.html | 2 +- .../wp-notifications-count.function.ts | 5 ++-- .../wp-full-view/wp-full-view.component.ts | 20 +++++++--------- .../routing/wp-full-view/wp-full-view.html | 2 +- .../wp-split-view/wp-split-view.component.ts | 24 ++++++++----------- .../routing/wp-split-view/wp-split-view.html | 2 +- 11 files changed, 54 insertions(+), 68 deletions(-) diff --git a/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts b/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts index 99ab65ed7f..8d4f4aa390 100644 --- a/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts +++ b/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts @@ -30,17 +30,16 @@ export class InAppNotificationCenterComponent implements OnInit { activeFacet$ = this.ianQuery.activeFacet$; notifications$ = this - .ianQuery + .ianService + .query .aggregatedNotifications$ .pipe( map((items) => Object.values(items)), ); - notificationsCount$ = this.ianQuery.selectCount(); + hasNotifications$ = this.ianService.query.hasNotifications$; - hasNotifications$ = this.ianQuery.hasNotifications$; - - hasMoreThanPageSize$ = this.ianQuery.hasMoreThanPageSize$; + hasMoreThanPageSize$ = this.ianService.query.hasMoreThanPageSize$; noResultText$ = this .activeFacet$ @@ -48,10 +47,16 @@ export class InAppNotificationCenterComponent implements OnInit { map((facet:'unread'|'all') => this.text.no_results[facet] || this.text.no_results.unread), ); - totalCountWarning$ = this.ianQuery.notLoaded$.pipe(map((notLoaded:number) => this.I18n.t( - 'js.notifications.center.total_count_warning', - { newest_count: NOTIFICATIONS_MAX_SIZE, more_count: notLoaded }, - ))); + totalCountWarning$ = this + .ianService + .query + .notLoaded$ + .pipe( + map((notLoaded:number) => this.I18n.t( + 'js.notifications.center.total_count_warning', + { newest_count: NOTIFICATIONS_MAX_SIZE, more_count: notLoaded }, + )), + ); maxSize = NOTIFICATIONS_MAX_SIZE; diff --git a/frontend/src/app/features/in-app-notifications/center/toolbar/facet/activate-facet-button.component.ts b/frontend/src/app/features/in-app-notifications/center/toolbar/facet/activate-facet-button.component.ts index 14c4e38181..a005c620de 100644 --- a/frontend/src/app/features/in-app-notifications/center/toolbar/facet/activate-facet-button.component.ts +++ b/frontend/src/app/features/in-app-notifications/center/toolbar/facet/activate-facet-button.component.ts @@ -30,6 +30,5 @@ export class ActivateFacetButtonComponent { activateFacet(facet:string):void { this.ianService.setActiveFacet(facet); - this.ianService.get(); } } diff --git a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts index 3220ed6797..2c51609ce0 100644 --- a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts +++ b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts @@ -13,7 +13,7 @@ import { InAppNotification, NOTIFICATIONS_MAX_SIZE } from './in-app-notification export class InAppNotificationsService { constructor( private store:InAppNotificationsStore, - private query:InAppNotificationsQuery, + public query:InAppNotificationsQuery, private apiV3Service:APIV3Service, private notifications:NotificationsService, ) { diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-mark-notification-button/work-package-mark-notification-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-mark-notification-button/work-package-mark-notification-button.component.ts index 0fc61f64c3..6dcfd1417d 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-mark-notification-button/work-package-mark-notification-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-mark-notification-button/work-package-mark-notification-button.component.ts @@ -1,23 +1,20 @@ import { - Component, ChangeDetectionStrategy, Input, OnInit, + Component, ChangeDetectionStrategy, Input, } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { InAppNotificationsService } from 'core-app/features/in-app-notifications/store/in-app-notifications.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; -import { InAppNotification } from 'core-app/features/in-app-notifications/store/in-app-notification.model'; @Component({ selector: 'op-work-package-mark-notification-button', templateUrl: './work-package-mark-notification-button.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class WorkPackageMarkNotificationButtonComponent implements OnInit { +export class WorkPackageMarkNotificationButtonComponent { @Input() public workPackage:WorkPackageResource; @Input() public buttonClasses:string; - private belongingNotifications:InAppNotification[]; - text = { mark_as_read: this.I18n.t('js.notifications.center.mark_as_read'), }; @@ -28,16 +25,7 @@ export class WorkPackageMarkNotificationButtonComponent implements OnInit { ) { } - ngOnInit():void { - this - .ianService - .notificationsOfWpLoaded - .subscribe((notifications) => { - this.belongingNotifications = notifications._embedded.elements; - }); - } - markAllBelongingWPsAsRead():void { - this.ianService.markAsRead(this.belongingNotifications); + this.ianService.markAllRead(); } } diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts index b4ad7ab6f8..2cfb1967c4 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts @@ -35,8 +35,10 @@ import { WorkPackagesActivityService } from 'core-app/features/work-packages/com import { I18nService } from 'core-app/core/i18n/i18n.service'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; -import { InAppNotification, NOTIFICATIONS_MAX_SIZE } from 'core-app/features/in-app-notifications/store/in-app-notification.model'; +import { InAppNotification } from 'core-app/features/in-app-notifications/store/in-app-notification.model'; import { InAppNotificationsService } from 'core-app/features/in-app-notifications/store/in-app-notifications.service'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; @Directive() export class ActivityPanelBaseController extends UntilDestroyedMixin implements OnInit { @@ -97,16 +99,9 @@ export class ActivityPanelBaseController extends UntilDestroyedMixin implements this.ianService.setActiveFacet('unread'); this.ianService.setActiveFilters([ - ['resourceId', '=', [workPackageId]], + ['resourceId', '=', [this.workPackageId]], ['resourceType', '=', ['WorkPackage']], ]); - this.ianService.fetchNotifications(); - - - this.ianService.loadNotificationsOfWorkPackage(this.workPackageId); - this.ianService.notificationsOfWpLoaded.subscribe((notificationCollection) => { - this.notifications = notificationCollection._embedded.elements; - }); } protected updateActivities(activities:HalResource[]) { @@ -138,8 +133,14 @@ export class ActivityPanelBaseController extends UntilDestroyedMixin implements .filter((activity:HalResource) => !!_.get(activity, 'comment.html')); } - protected hasUnreadNotification(activityHref:string):boolean { - return !!this.notifications.find((notification) => notification._links.activity?.href === activityHref); + protected hasUnreadNotification(activityHref:string):Observable { + return this + .ianService + .query + .unread$ + .pipe( + map((notifications) => !!notifications.find((notification) => notification._links.activity?.href === activityHref)), + ); } public toggleComments() { diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-tab.html b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-tab.html index 79e9437e83..dc32983e55 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-tab.html +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-tab.html @@ -28,7 +28,7 @@ [workPackage]="workPackage" [activity]="inf.activity" [activityNo]="inf.number(inf.isReversed)" - [hasUnreadNotification]="hasUnreadNotification(inf.href)" + [hasUnreadNotification]="hasUnreadNotification(inf.href) | async" [isInitial]="inf.isInitial()" > diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-notifications-count.function.ts b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-notifications-count.function.ts index 760cb61a80..8137662cdc 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-notifications-count.function.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-notifications-count.function.ts @@ -12,9 +12,8 @@ export function workPackageNotificationsCount( ):Observable { const ianService = injector.get(InAppNotificationsService); - return ianService - .notificationsOfWpLoaded + return ianService.query.unread$ .pipe( - map((data) => data._embedded.elements.length), + map((data) => data.length), ); } diff --git a/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts b/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts index e548fa6e23..63a58f8afb 100644 --- a/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts @@ -29,7 +29,7 @@ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { StateService } from '@uirouter/core'; import { Component, Injector, OnInit } from '@angular/core'; -import { of } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; import { WorkPackageSingleViewBase } from 'core-app/features/work-packages/routing/wp-view-base/work-package-single-view.base'; import { InAppNotificationsQuery } from 'core-app/features/in-app-notifications/store/in-app-notifications.query'; @@ -37,7 +37,6 @@ import { InAppNotificationsStore } from 'core-app/features/in-app-notifications/ import { InAppNotificationsService } from 'core-app/features/in-app-notifications/store/in-app-notifications.service'; import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; -import { InAppNotificationsService } from 'core-app/features/in-app-notifications/store/in-app-notifications.service'; @Component({ templateUrl: './wp-full-view.html', @@ -57,7 +56,7 @@ export class WorkPackagesFullViewComponent extends WorkPackageSingleViewBase imp public displayWatchButton:boolean; - displayNotificationsButton = false; + public displayNotificationsButton$:Observable = this.ianService.query.hasNotifications$; public watchers:any; @@ -90,7 +89,13 @@ export class WorkPackagesFullViewComponent extends WorkPackageSingleViewBase imp // Set Focused WP this.wpTableFocus.updateFocus(this.workPackage.id!); - this.ianService.loadNotificationsOfWorkPackage(this.workPackage.id || ''); + if (this.workPackage.id) { + this.ianService.setActiveFacet('unread'); + this.ianService.setActiveFilters([ + ['resourceId', '=', [this.workPackage.id]], + ['resourceType', '=', ['WorkPackage']], + ]); + } this.setWorkPackageScopeProperties(this.workPackage); } @@ -99,13 +104,6 @@ export class WorkPackagesFullViewComponent extends WorkPackageSingleViewBase imp this.isWatched = wp.hasOwnProperty('unwatch'); this.displayWatchButton = wp.hasOwnProperty('unwatch') || wp.hasOwnProperty('watch'); - this - .ianService - .notificationsOfWpLoaded - .subscribe((notifications) => { - this.displayNotificationsButton = notifications.count > 0; - }); - // watchers if (wp.watchers) { this.watchers = (wp.watchers as any).elements; diff --git a/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.html b/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.html index 8394796944..024b2bdf8e 100644 --- a/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.html +++ b/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.html @@ -30,7 +30,7 @@ [showText]="false"> -
  • +
  • = this.ianService.query.hasNotifications$; constructor( public injector:Injector, @@ -108,11 +106,13 @@ export class WorkPackageSplitViewComponent extends WorkPackageSingleViewBase imp } }); - this.ianService.loadNotificationsOfWorkPackage(wpId.toString()); - - this.ianService.notificationsOfWpLoaded.subscribe((notifications) => { - this.displayNotificationsButton = notifications.count > 0; - }); + if (wpId) { + this.ianService.setActiveFacet('unread'); + this.ianService.setActiveFilters([ + ['resourceId', '=', [wpId]], + ['resourceType', '=', ['WorkPackage']], + ]); + } } get shouldFocus():boolean { @@ -126,8 +126,4 @@ export class WorkPackageSplitViewComponent extends WorkPackageSingleViewBase imp backToList():void { this.backRouting.goToBaseState(); } - - showNotificationsButton():boolean { - return this.displayNotificationsButton && this.keepTab.currentTabIdentifier === 'activity'; - } } diff --git a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html index cfbe403432..08463a9e72 100644 --- a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html +++ b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html @@ -10,7 +10,7 @@ Date: Tue, 24 Aug 2021 16:17:20 +0200 Subject: [PATCH 10/17] Get notification count in tab headings via local injector --- .../tabs/global-search-tabs.component.ts | 16 ++++++++++++---- .../in-app-notification-center-page.component.ts | 8 ++++++++ .../in-app-notification-center.component.ts | 10 ++-------- .../wp-tabs/services/wp-tabs/wp-tabs.service.ts | 7 +++++-- .../tabs/content-tabs/content-tabs.component.ts | 15 +++++++++++---- .../scrollable-tabs.component.html | 4 ++-- .../scrollable-tabs/scrollable-tabs.component.ts | 7 +++++-- .../tabs/tab-badges/tab-count.component.html | 2 +- .../tabs/tab-badges/tab-count.component.ts | 2 +- .../app/shared/components/tabs/tab.interface.ts | 3 ++- 10 files changed, 49 insertions(+), 25 deletions(-) diff --git a/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts b/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts index 8544c36740..7e20ddb721 100644 --- a/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts +++ b/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts @@ -26,7 +26,12 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectorRef, Component, OnDestroy } from '@angular/core'; +import { + ChangeDetectorRef, + Component, + OnDestroy, + Injector, +} from '@angular/core'; import { Subscription } from 'rxjs'; import { GlobalSearchService } from 'core-app/core/global_search/services/global-search.service'; import { ScrollableTabsComponent } from 'core-app/shared/components/tabs/scrollable-tabs/scrollable-tabs.component'; @@ -46,9 +51,12 @@ export class GlobalSearchTabsComponent extends ScrollableTabsComponent implement public classes:string[] = ['global-search--tabs', 'scrollable-tabs']; - constructor(readonly globalSearchService:GlobalSearchService, - cdRef:ChangeDetectorRef) { - super(cdRef); + constructor( + readonly globalSearchService:GlobalSearchService, + public injector: Injector, + cdRef:ChangeDetectorRef, + ) { + super(cdRef, injector); } ngOnInit() { diff --git a/frontend/src/app/features/in-app-notifications/center/in-app-notification-center-page.component.ts b/frontend/src/app/features/in-app-notifications/center/in-app-notification-center-page.component.ts index e71874709e..36a5e710fa 100644 --- a/frontend/src/app/features/in-app-notifications/center/in-app-notification-center-page.component.ts +++ b/frontend/src/app/features/in-app-notifications/center/in-app-notification-center-page.component.ts @@ -20,6 +20,9 @@ import { NotificationSettingsButtonComponent } from 'core-app/features/in-app-no import { ActivateFacetButtonComponent } from 'core-app/features/in-app-notifications/center/toolbar/facet/activate-facet-button.component'; import { MarkAllAsReadButtonComponent } from 'core-app/features/in-app-notifications/center/toolbar/mark-all-as-read/mark-all-as-read-button.component'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { InAppNotificationsQuery } from 'core-app/features/in-app-notifications/store/in-app-notifications.query'; +import { InAppNotificationsStore } from 'core-app/features/in-app-notifications/store/in-app-notifications.store'; +import { InAppNotificationsService } from 'core-app/features/in-app-notifications/store/in-app-notifications.service'; import { BackRouteOptions, BackRoutingService, @@ -31,6 +34,11 @@ import { '../../work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component.sass', ], changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + InAppNotificationsService, + InAppNotificationsStore, + InAppNotificationsQuery, + ], }) export class InAppNotificationCenterPageComponent extends UntilDestroyedMixin implements OnInit { text = { diff --git a/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts b/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts index 8d4f4aa390..96b420d7fd 100644 --- a/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts +++ b/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts @@ -6,12 +6,11 @@ import { OnInit, } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { InAppNotificationsQuery } from 'core-app/features/in-app-notifications/store/in-app-notifications.query'; -import { InAppNotificationsStore } from 'core-app/features/in-app-notifications/store/in-app-notifications.store'; -import { InAppNotificationsService } from 'core-app/features/in-app-notifications/store/in-app-notifications.service'; import { NOTIFICATIONS_MAX_SIZE } from 'core-app/features/in-app-notifications/store/in-app-notification.model'; import { map } from 'rxjs/operators'; import { StateService } from '@uirouter/angular'; +import { InAppNotificationsQuery } from 'core-app/features/in-app-notifications/store/in-app-notifications.query'; +import { InAppNotificationsService } from 'core-app/features/in-app-notifications/store/in-app-notifications.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { UIRouterGlobals } from '@uirouter/core'; @@ -20,11 +19,6 @@ import { UIRouterGlobals } from '@uirouter/core'; templateUrl: './in-app-notification-center.component.html', styleUrls: ['./in-app-notification-center.component.sass'], changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - InAppNotificationsService, - InAppNotificationsStore, - InAppNotificationsQuery, - ], }) export class InAppNotificationCenterComponent implements OnInit { activeFacet$ = this.ianQuery.activeFacet$; diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts index 04dc523284..3c67496c8f 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts @@ -1,8 +1,9 @@ import { Injectable, Injector } from '@angular/core'; +import { from } from 'rxjs'; +import { StateService } from '@uirouter/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { WpTabDefinition } from 'core-app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/tab'; import { WorkPackageRelationsTabComponent } from 'core-app/features/work-packages/components/wp-single-view-tabs/relations-tab/relations-tab.component'; -import { StateService } from '@uirouter/core'; import { WorkPackageOverviewTabComponent } from 'core-app/features/work-packages/components/wp-single-view-tabs/overview-tab/overview-tab.component'; import { WorkPackageActivityTabComponent } from 'core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-tab.component'; import { WorkPackageWatchersTabComponent } from 'core-app/features/work-packages/components/wp-single-view-tabs/watchers-tab/watchers-tab.component'; @@ -45,7 +46,9 @@ export class WorkPackageTabsService { .map( (tab) => ({ ...tab, - ...!!tab.count && { counter: tab.count(workPackage, this.injector) }, + counter: tab.count + ? (injector:Injector) => tab.count!(workPackage, injector || this.injector) + : (_:Injector) => from([0]), }), ); } diff --git a/frontend/src/app/shared/components/tabs/content-tabs/content-tabs.component.ts b/frontend/src/app/shared/components/tabs/content-tabs/content-tabs.component.ts index fd12ac70e1..aac2ed5342 100644 --- a/frontend/src/app/shared/components/tabs/content-tabs/content-tabs.component.ts +++ b/frontend/src/app/shared/components/tabs/content-tabs/content-tabs.component.ts @@ -27,7 +27,11 @@ //++ import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ElementRef, + Injector, } from '@angular/core'; import { GonService } from 'core-app/core/gon/gon.service'; import { StateService } from '@uirouter/core'; @@ -52,12 +56,15 @@ interface GonTab extends TabDefinition { export class ContentTabsComponent extends ScrollableTabsComponent { public classes:string[] = ['content--tabs', 'scrollable-tabs']; - constructor(readonly elementRef:ElementRef, + constructor( + readonly elementRef:ElementRef, readonly $state:StateService, readonly gon:GonService, cdRef:ChangeDetectorRef, - readonly I18n:I18nService) { - super(cdRef); + readonly I18n:I18nService, + public injector: Injector, + ) { + super(cdRef, injector); const gonTabs = JSON.parse((this.gon.get('contentTabs') as any).tabs); const currentTab = JSON.parse((this.gon.get('contentTabs') as any).selected); diff --git a/frontend/src/app/shared/components/tabs/scrollable-tabs/scrollable-tabs.component.html b/frontend/src/app/shared/components/tabs/scrollable-tabs/scrollable-tabs.component.html index 481c82ab45..e3e46bbb9e 100644 --- a/frontend/src/app/shared/components/tabs/scrollable-tabs/scrollable-tabs.component.html +++ b/frontend/src/app/shared/components/tabs/scrollable-tabs/scrollable-tabs.component.html @@ -38,10 +38,10 @@ - + + ; + @Input('count') count:number; } diff --git a/frontend/src/app/shared/components/tabs/tab.interface.ts b/frontend/src/app/shared/components/tabs/tab.interface.ts index f179673690..bc7da26d5a 100644 --- a/frontend/src/app/shared/components/tabs/tab.interface.ts +++ b/frontend/src/app/shared/components/tabs/tab.interface.ts @@ -1,4 +1,5 @@ import { Observable } from 'rxjs'; +import { Injector } from '@angular/core'; export interface TabDefinition { /** Internal identifier of the tab */ @@ -12,7 +13,7 @@ export interface TabDefinition { /** UI router params to use uiParams with */ routeParams?:unknown; /** Show a tab count with this observable's result */ - counter?:Observable; + counter?:(injector?:Injector) => Observable; /** Whether the counter should be shown as number in brackets or within a bubble */ showCountAsBubble?:boolean; /** Disable the tab, optionally with an explanatory title */ From 455f5eb164d6b7eeae7350238f7f3e106a79fad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Tue, 24 Aug 2021 17:02:33 +0200 Subject: [PATCH 11/17] Change some notification specs --- .../bell/in-app-notification-bell.component.html | 11 +++++------ .../wp-tabs/services/wp-tabs/wp-tabs.service.spec.ts | 2 +- .../notification_center/notification_center_spec.rb | 4 +++- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.html b/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.html index 83a445e898..6c7fc8ca1e 100644 --- a/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.html +++ b/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.html @@ -2,15 +2,14 @@ class="op-ian-bell op-app-menu--item-action" data-qa-selector="op-ian-bell" [href]="notificationsPath()" - > - - +> + - - + [textContent]="unreadCount > 99 ? '' : unreadCount" + > \ No newline at end of file diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.spec.ts b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.spec.ts index 352971167b..03df750322 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.spec.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.spec.ts @@ -50,7 +50,7 @@ describe('WpTabsService', () => { describe('displayableTabs()', () => { it('returns just the displayable tab', () => { - expect(service.getDisplayableTabs(workPackage)).toEqual([displayableTab]); + expect(service.getDisplayableTabs(workPackage)[0].id).toEqual(displayableTab.id); }); }); diff --git a/spec/features/notifications/notification_center/notification_center_spec.rb b/spec/features/notifications/notification_center/notification_center_spec.rb index 616bd2e1db..8e75358d79 100644 --- a/spec/features/notifications/notification_center/notification_center_spec.rb +++ b/spec/features/notifications/notification_center/notification_center_spec.rb @@ -60,12 +60,14 @@ describe "Notification center", type: :feature, js: true do center.expect_work_package_item second_notification center.mark_all_read - center.expect_bell_count 0 notification.reload expect(notification.read_ian).to be_truthy center.expect_no_item notification center.expect_no_item second_notification + + center.open + center.expect_bell_count 0 end it 'can open the split screen of the notification' do From ed9669265a1ef4bd8ae6aab0fcd88d4526b5e2b2 Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Wed, 25 Aug 2021 08:12:18 +0200 Subject: [PATCH 12/17] Correct highlighting of unread notifications of activities on overview tab, stabilise tests and fix some EsLint issues --- .../tabs/global-search-tabs.component.ts | 2 +- .../store/in-app-notifications.service.ts | 14 +++++++++++--- .../activity-panel/activity-base.controller.ts | 3 +-- .../activity-panel/activity-on-overview.html | 2 +- .../wp-split-view/wp-split-view.component.ts | 7 ++++++- .../tabs/content-tabs/content-tabs.component.ts | 2 +- .../tabs/tab-badges/tab-count.component.ts | 3 +-- .../notification_center_spec.rb | 13 ++++++++++--- 8 files changed, 32 insertions(+), 14 deletions(-) diff --git a/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts b/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts index 7e20ddb721..07e39af9b9 100644 --- a/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts +++ b/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts @@ -53,7 +53,7 @@ export class GlobalSearchTabsComponent extends ScrollableTabsComponent implement constructor( readonly globalSearchService:GlobalSearchService, - public injector: Injector, + public injector:Injector, cdRef:ChangeDetectorRef, ) { super(cdRef, injector); diff --git a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts index 2c51609ce0..956008290c 100644 --- a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts +++ b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts @@ -1,5 +1,11 @@ import { Injectable } from '@angular/core'; -import { map, switchMap, tap, take, debounceTime } from 'rxjs/operators'; +import { + debounceTime, + map, + switchMap, + take, + tap, +} from 'rxjs/operators'; import { applyTransaction, ID, setLoading } from '@datorama/akita'; import { ApiV3ListFilter } from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @@ -8,6 +14,8 @@ import { InAppNotificationsQuery } from 'core-app/features/in-app-notifications/ import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { InAppNotificationsStore } from './in-app-notifications.store'; import { InAppNotification, NOTIFICATIONS_MAX_SIZE } from './in-app-notification.model'; +import { Observable } from 'rxjs'; +import { IHALCollection } from 'core-app/core/apiv3/types/hal-collection.type'; @Injectable() export class InAppNotificationsService { @@ -25,7 +33,7 @@ export class InAppNotificationsService { }); } - fetchNotifications() { + fetchNotifications():Observable> { this.store.setLoading(true); const { activeFacet, activeFilters } = this.query.getValue(); @@ -54,7 +62,7 @@ export class InAppNotificationsService { return call; } - fetchCount() { + fetchCount():Observable { const { activeFilters } = this.query.getValue(); return this diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts index 2cfb1967c4..c48ce66f61 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts @@ -81,7 +81,7 @@ export class ActivityPanelBaseController extends UntilDestroyedMixin implements this.togglerText = this.text.commentsOnly; } - ngOnInit() { + ngOnInit():void { this .apiV3Service .work_packages @@ -96,7 +96,6 @@ export class ActivityPanelBaseController extends UntilDestroyedMixin implements }); }); - this.ianService.setActiveFacet('unread'); this.ianService.setActiveFilters([ ['resourceId', '=', [this.workPackageId]], diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-on-overview.html b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-on-overview.html index bf663c8dc1..662f426550 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-on-overview.html +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-on-overview.html @@ -6,7 +6,7 @@ diff --git a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts index 989d6d3f5d..1c80dd00d0 100644 --- a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts @@ -26,7 +26,12 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectionStrategy, Component, Injector, OnInit } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + Injector, + OnInit, +} from '@angular/core'; import { StateService } from '@uirouter/core'; import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service'; import { States } from 'core-app/core/states/states.service'; diff --git a/frontend/src/app/shared/components/tabs/content-tabs/content-tabs.component.ts b/frontend/src/app/shared/components/tabs/content-tabs/content-tabs.component.ts index aac2ed5342..b936bbeaad 100644 --- a/frontend/src/app/shared/components/tabs/content-tabs/content-tabs.component.ts +++ b/frontend/src/app/shared/components/tabs/content-tabs/content-tabs.component.ts @@ -62,7 +62,7 @@ export class ContentTabsComponent extends ScrollableTabsComponent { readonly gon:GonService, cdRef:ChangeDetectorRef, readonly I18n:I18nService, - public injector: Injector, + public injector:Injector, ) { super(cdRef, injector); diff --git a/frontend/src/app/shared/components/tabs/tab-badges/tab-count.component.ts b/frontend/src/app/shared/components/tabs/tab-badges/tab-count.component.ts index 4914a21dcb..d6474a204e 100644 --- a/frontend/src/app/shared/components/tabs/tab-badges/tab-count.component.ts +++ b/frontend/src/app/shared/components/tabs/tab-badges/tab-count.component.ts @@ -1,5 +1,4 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { Observable } from 'rxjs'; @Component({ selector: 'op-tab-count', @@ -8,5 +7,5 @@ import { Observable } from 'rxjs'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class TabCountComponent { - @Input('count') count:number; + @Input() count:number; } diff --git a/spec/features/notifications/notification_center/notification_center_spec.rb b/spec/features/notifications/notification_center/notification_center_spec.rb index 8e75358d79..649f82a7c1 100644 --- a/spec/features/notifications/notification_center/notification_center_spec.rb +++ b/spec/features/notifications/notification_center/notification_center_spec.rb @@ -60,8 +60,10 @@ describe "Notification center", type: :feature, js: true do center.expect_work_package_item second_notification center.mark_all_read - notification.reload - expect(notification.read_ian).to be_truthy + retry_block do + notification.reload + raise "Expected notification to be marked read" unless notification.read_ian + end center.expect_no_item notification center.expect_no_item second_notification @@ -141,7 +143,12 @@ describe "Notification center", type: :feature, js: true do second_split_screen.expect_open center.mark_notification_as_read third_notification - expect(second_notification.reload.read_ian).to be_truthy + + retry_block do + second_notification.reload + raise "Expected notification to be marked read" unless second_notification.read_ian + end + expect(third_notification.reload.read_ian).to be_truthy end end From 647b0928e676b8eb0c0f38f7caa7f03aa7fb8f34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Wed, 25 Aug 2021 10:06:54 +0200 Subject: [PATCH 13/17] Add eslint ignore comments for wp-tabs service lines --- .../components/wp-tabs/services/wp-tabs/wp-tabs.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts index 3c67496c8f..a8d987dc6b 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts @@ -47,8 +47,8 @@ export class WorkPackageTabsService { (tab) => ({ ...tab, counter: tab.count - ? (injector:Injector) => tab.count!(workPackage, injector || this.injector) - : (_:Injector) => from([0]), + ? (injector:Injector) => tab.count!(workPackage, injector || this.injector) // eslint-disable-line no-non-null-assertion + : (_:Injector) => from([0]), // eslint-disable-line no-unused-vars }), ); } From feb540a43e051ce64b58c627f50e1afcaa1ffb1a Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Wed, 25 Aug 2021 11:35:05 +0200 Subject: [PATCH 14/17] Reduce min-width of split screen to 530p again as the original increase reason is obsolete due to the refactoringof the split view design --- .../bell/in-app-notification-bell.component.ts | 2 +- .../in-app-notifications/store/in-app-notifications.query.ts | 2 +- .../store/in-app-notifications.service.ts | 4 ++-- .../work-package-mark-notification-button.component.html | 2 +- .../work-package-mark-notification-button.component.ts | 2 +- .../activity-panel/activity-base.controller.ts | 2 +- .../shared/components/resizer/resizer/wp-resizer.component.ts | 2 +- .../src/global_styles/layout/work_packages/_details_view.sass | 4 ++-- .../src/global_styles/layout/work_packages/_full_view.sass | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts b/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts index 59ca53ab78..425a90a651 100644 --- a/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts +++ b/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts @@ -26,7 +26,7 @@ export class InAppNotificationBellComponent { polling$ = timer(10, POLLING_INTERVAL) .pipe( filter(() => this.activeWindow.isActive), - switchMap(() => this.inAppService.fetchCount()), + switchMap(() => this.inAppService.fetchUnreadCount()), ); unreadCount$ = merge( diff --git a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.query.ts b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.query.ts index adaf78acc6..0573d9e3a2 100644 --- a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.query.ts +++ b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.query.ts @@ -61,7 +61,7 @@ export class InAppNotificationsQuery extends QueryEntity count > 0)); - /** Determine whether the pageSize is not sufficient to load all notifcations */ + /** Determine whether the pageSize is not sufficient to load all notifications */ hasMoreThanPageSize$ = this .select() .pipe( diff --git a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts index 956008290c..f3c40b7ae0 100644 --- a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts +++ b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts @@ -29,7 +29,7 @@ export class InAppNotificationsService { .pipe(debounceTime(0)) .subscribe(() => { this.fetchNotifications(); - this.fetchCount(); + this.fetchUnreadCount(); }); } @@ -62,7 +62,7 @@ export class InAppNotificationsService { return call; } - fetchCount():Observable { + fetchUnreadCount():Observable { const { activeFilters } = this.query.getValue(); return this diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-mark-notification-button/work-package-mark-notification-button.component.html b/frontend/src/app/features/work-packages/components/wp-buttons/wp-mark-notification-button/work-package-mark-notification-button.component.html index 589621eb93..f76a5e0ee4 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-mark-notification-button/work-package-mark-notification-button.component.html +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-mark-notification-button/work-package-mark-notification-button.component.html @@ -1,5 +1,5 @@
  • diff --git a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts index 1c80dd00d0..ac9d31fcdc 100644 --- a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts @@ -62,7 +62,7 @@ export class WorkPackageSplitViewComponent extends WorkPackageSingleViewBase imp /** Reference to the base route e.g., work-packages.partitioned.list or bim.partitioned.split */ private baseRoute:string = this.$state.current.data.baseRoute; - public displayNotificationsButton$:Observable = this.ianService.query.hasNotifications$; + public displayNotificationsButton$:Observable = this.ianService.query.hasUnread$; constructor( public injector:Injector, From 2e16c079b6f2a471f2570b0523c56a88d8261ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Wed, 25 Aug 2021 14:26:22 +0200 Subject: [PATCH 16/17] Fix eslint errors outside of specs --- .../in-app-notification-bell.component.ts | 21 +++++++++++-------- .../store/in-app-notifications.service.ts | 7 ++++--- .../services/wp-tabs/wp-tabs.service.ts | 4 ++-- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts b/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts index db70a2b689..9dd1f44eb0 100644 --- a/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts +++ b/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts @@ -4,7 +4,12 @@ import { InAppNotificationsStore } from 'core-app/features/in-app-notifications/ import { InAppNotificationsService } from 'core-app/features/in-app-notifications/store/in-app-notifications.service'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; import { timer, combineLatest } from 'rxjs'; -import { filter, switchMap, tap, map } from 'rxjs/operators'; +import { + filter, + switchMap, + tap, + map, +} from 'rxjs/operators'; import { ActiveWindowService } from 'core-app/core/active-window/active-window.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @@ -23,12 +28,11 @@ const POLLING_INTERVAL = 10000; ], }) export class InAppNotificationBellComponent implements OnInit { - polling$ = timer(10, POLLING_INTERVAL) - .pipe( - filter(() => this.activeWindow.isActive), - tap(() => console.log('sending fetch request from bell')), - switchMap(() => this.inAppService.fetchNotifications()), - ); + polling$ = timer(10, POLLING_INTERVAL).pipe( + filter(() => this.activeWindow.isActive), + tap(() => console.log('sending fetch request from bell')), + switchMap(() => this.inAppService.fetchNotifications()), + ); unreadCount$ = combineLatest([ this.inAppQuery.notLoaded$, @@ -43,9 +47,8 @@ export class InAppNotificationBellComponent implements OnInit { readonly pathHelper:PathHelperService, ) { } - ngOnInit() { + ngOnInit():void { this.inAppService.setPageSize(0); - console.log('ngoninit ian bell'); } notificationsPath():string { diff --git a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts index e7b1b74680..41b4cc26ab 100644 --- a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts +++ b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts @@ -6,6 +6,7 @@ import { tap, catchError, } from 'rxjs/operators'; +import { Subscription } from 'rxjs'; import { applyTransaction, ID, setLoading } from '@datorama/akita'; import { ApiV3ListFilter } from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @@ -80,7 +81,7 @@ export class InAppNotificationsService { this.store.update((state) => ({ ...state, activeFilters: filters })); } - markAllRead() { + markAllRead():Subscription { return this.query .unread$ .pipe( @@ -96,7 +97,7 @@ export class InAppNotificationsService { }); } - markAsRead(notifications:InAppNotification[], keep = false) { + markAsRead(notifications:InAppNotification[], keep = false):Subscription { const ids = notifications.map((n) => n.id); return this @@ -116,7 +117,7 @@ export class InAppNotificationsService { }); } - private sideLoadInvolvedWorkPackages(elements:InAppNotification[]) { + private sideLoadInvolvedWorkPackages(elements:InAppNotification[]):void { const wpIds = elements.map((element) => { const href = element._links.resource?.href; return href && HalResource.matchFromLink(href, 'work_packages'); diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts index a8d987dc6b..f9c11f7303 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts @@ -47,8 +47,8 @@ export class WorkPackageTabsService { (tab) => ({ ...tab, counter: tab.count - ? (injector:Injector) => tab.count!(workPackage, injector || this.injector) // eslint-disable-line no-non-null-assertion - : (_:Injector) => from([0]), // eslint-disable-line no-unused-vars + ? (injector:Injector) => tab.count!(workPackage, injector || this.injector) // eslint-disable-line @typescript-eslint/no-non-null-assertion + : (_:Injector) => from([0]), // eslint-disable-line @typescript-eslint/no-unused-vars }), ); } From d44ed5f1ab96a00dd9176c7832bdaaa2b99cb82a Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Thu, 26 Aug 2021 07:39:20 +0200 Subject: [PATCH 17/17] Fix linting issues --- .../bell/in-app-notification-bell.component.ts | 2 -- .../in-app-notifications/store/in-app-notifications.service.ts | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts b/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts index 9dd1f44eb0..9d0570947f 100644 --- a/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts +++ b/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts @@ -7,7 +7,6 @@ import { timer, combineLatest } from 'rxjs'; import { filter, switchMap, - tap, map, } from 'rxjs/operators'; import { ActiveWindowService } from 'core-app/core/active-window/active-window.service'; @@ -30,7 +29,6 @@ const POLLING_INTERVAL = 10000; export class InAppNotificationBellComponent implements OnInit { polling$ = timer(10, POLLING_INTERVAL).pipe( filter(() => this.activeWindow.isActive), - tap(() => console.log('sending fetch request from bell')), switchMap(() => this.inAppService.fetchNotifications()), ); diff --git a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts index 41b4cc26ab..bfb993c401 100644 --- a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts +++ b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts @@ -6,7 +6,7 @@ import { tap, catchError, } from 'rxjs/operators'; -import { Subscription } from 'rxjs'; +import { Subscription, Observable } from 'rxjs'; import { applyTransaction, ID, setLoading } from '@datorama/akita'; import { ApiV3ListFilter } from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @@ -15,7 +15,6 @@ import { InAppNotificationsQuery } from 'core-app/features/in-app-notifications/ import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { InAppNotificationsStore } from './in-app-notifications.store'; import { InAppNotification } from './in-app-notification.model'; -import { Observable } from 'rxjs'; import { IHALCollection } from 'core-app/core/apiv3/types/hal-collection.type'; @Injectable()