diff --git a/projects/aca-content/src/lib/components/search/search-input/search-input.component.spec.ts b/projects/aca-content/src/lib/components/search/search-input/search-input.component.spec.ts index 19c8046df..62c6947b2 100644 --- a/projects/aca-content/src/lib/components/search/search-input/search-input.component.spec.ts +++ b/projects/aca-content/src/lib/components/search/search-input/search-input.component.spec.ts @@ -27,12 +27,14 @@ import { TestBed, ComponentFixture } from '@angular/core/testing'; import { SearchInputComponent } from './search-input.component'; import { AppTestingModule } from '../../../testing/app-testing.module'; import { Actions, ofType } from '@ngrx/effects'; -import { SearchByTermAction, SearchActionTypes, SnackbarErrorAction, SnackbarActionTypes } from '@alfresco/aca-shared/store'; +import { SearchByTermAction, SearchActionTypes } from '@alfresco/aca-shared/store'; import { AppHookService, AppService } from '@alfresco/aca-shared'; import { map } from 'rxjs/operators'; import { SearchQueryBuilderService } from '@alfresco/adf-content-services'; import { SearchNavigationService } from '../search-navigation.service'; import { BehaviorSubject, Subject } from 'rxjs'; +import { NotificationService } from '@alfresco/adf-core'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; describe('SearchInputComponent', () => { let fixture: ComponentFixture; @@ -40,6 +42,8 @@ describe('SearchInputComponent', () => { let actions$: Actions; let appHookService: AppHookService; let searchInputService: SearchNavigationService; + let showErrorSpy: jasmine.Spy; + const appServiceMock = { appNavNarMode$: new BehaviorSubject('collapsed'), setAppNavbarMode: jasmine.createSpy('setAppNavbarMode'), @@ -49,7 +53,7 @@ describe('SearchInputComponent', () => { beforeEach(() => { appServiceMock.setAppNavbarMode.calls.reset(); TestBed.configureTestingModule({ - imports: [AppTestingModule, SearchInputComponent], + imports: [AppTestingModule, SearchInputComponent, MatSnackBarModule], providers: [ { provide: AppService, @@ -65,6 +69,9 @@ describe('SearchInputComponent', () => { appHookService = TestBed.inject(AppHookService); searchInputService = TestBed.inject(SearchNavigationService); component = fixture.componentInstance; + + const notificationService = TestBed.inject(NotificationService); + showErrorSpy = spyOn(notificationService, 'showError'); }); afterEach(() => { @@ -171,19 +178,9 @@ describe('SearchInputComponent', () => { component.onSearchChange(searchedTerm); }); - it('should show snack for empty search', (done) => { - const searchedTerm = ''; - actions$ - .pipe( - ofType(SnackbarActionTypes.Error), - map((action) => { - expect(action.payload).toBe('APP.BROWSE.SEARCH.EMPTY_SEARCH'); - }) - ) - .subscribe(() => { - done(); - }); - component.onSearchSubmit(searchedTerm); + it('should show snack for empty search', () => { + component.onSearchSubmit(''); + expect(showErrorSpy).toHaveBeenCalled(); }); }); diff --git a/projects/aca-content/src/lib/components/search/search-input/search-input.component.ts b/projects/aca-content/src/lib/components/search/search-input/search-input.component.ts index 484b938d8..48ac7da33 100644 --- a/projects/aca-content/src/lib/components/search/search-input/search-input.component.ts +++ b/projects/aca-content/src/lib/components/search/search-input/search-input.component.ts @@ -23,10 +23,10 @@ */ import { AppHookService, AppService } from '@alfresco/aca-shared'; -import { AppStore, SearchByTermAction, SearchOptionIds, SearchOptionModel, SnackbarErrorAction } from '@alfresco/aca-shared/store'; +import { AppStore, SearchByTermAction, SearchOptionIds, SearchOptionModel } from '@alfresco/aca-shared/store'; import { SearchQueryBuilderService } from '@alfresco/adf-content-services'; -import { AppConfigService } from '@alfresco/adf-core'; -import { Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; +import { AppConfigService, NotificationService } from '@alfresco/adf-core'; +import { Component, inject, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu'; import { NavigationEnd, PRIMARY_OUTLET, Router, RouterEvent, UrlSegment, UrlSegmentGroup, UrlTree } from '@angular/router'; import { Store } from '@ngrx/store'; @@ -67,6 +67,8 @@ import { FormsModule } from '@angular/forms'; host: { class: 'aca-search-input' } }) export class SearchInputComponent implements OnInit, OnDestroy { + private notificationService = inject(NotificationService); + onDestroy$: Subject = new Subject(); hasOneChange = false; hasNewChange = false; @@ -174,7 +176,7 @@ export class SearchInputComponent implements OnInit, OnDestroy { this.searchByOption(); } else { - this.store.dispatch(new SnackbarErrorAction('APP.BROWSE.SEARCH.EMPTY_SEARCH')); + this.notificationService.showError('APP.BROWSE.SEARCH.EMPTY_SEARCH'); } if (this.trigger) { diff --git a/projects/aca-content/src/lib/components/search/search-results/search-results.component.spec.ts b/projects/aca-content/src/lib/components/search/search-results/search-results.component.spec.ts index aff8bb7da..72425d502 100644 --- a/projects/aca-content/src/lib/components/search/search-results/search-results.component.spec.ts +++ b/projects/aca-content/src/lib/components/search/search-results/search-results.component.spec.ts @@ -24,9 +24,9 @@ import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { SearchResultsComponent } from './search-results.component'; -import { AppConfigService, TranslationService } from '@alfresco/adf-core'; +import { AppConfigService, NotificationService, TranslationService } from '@alfresco/adf-core'; import { Store } from '@ngrx/store'; -import { NavigateToFolder, SnackbarErrorAction } from '@alfresco/aca-shared/store'; +import { NavigateToFolder } from '@alfresco/aca-shared/store'; import { Pagination, SearchRequest } from '@alfresco/js-api'; import { SearchQueryBuilderService } from '@alfresco/adf-content-services'; import { ActivatedRoute, Router } from '@angular/router'; @@ -44,6 +44,7 @@ describe('SearchComponent', () => { let router: Router; const searchRequest = {} as SearchRequest; let params: BehaviorSubject; + let showErrorSpy: jasmine.Spy; beforeEach(() => { params = new BehaviorSubject({ q: 'TYPE: "cm:folder" AND %28=cm: name: email OR cm: name: budget%29' }); @@ -78,6 +79,9 @@ describe('SearchComponent', () => { translate = TestBed.inject(TranslationService); router = TestBed.inject(Router); + const notificationService = TestBed.inject(NotificationService); + showErrorSpy = spyOn(notificationService, 'showError'); + config.config = { search: {} }; @@ -103,7 +107,7 @@ describe('SearchComponent', () => { queryBuilder.execute(); tick(); - expect(store.dispatch).toHaveBeenCalledWith(new SnackbarErrorAction('APP.BROWSE.SEARCH.ERRORS.GENERIC')); + expect(showErrorSpy).toHaveBeenCalledWith('APP.BROWSE.SEARCH.ERRORS.GENERIC'); })); it('should raise a known error if search fails', fakeAsync(() => { @@ -122,7 +126,7 @@ describe('SearchComponent', () => { queryBuilder.execute(); tick(); - expect(store.dispatch).toHaveBeenCalledWith(new SnackbarErrorAction('Known Error')); + expect(showErrorSpy).toHaveBeenCalledWith('Known Error'); })); it('should raise a generic error if search fails', fakeAsync(() => { @@ -141,7 +145,7 @@ describe('SearchComponent', () => { queryBuilder.execute(); tick(); - expect(store.dispatch).toHaveBeenCalledWith(new SnackbarErrorAction('Generic Error')); + expect(showErrorSpy).toHaveBeenCalledWith('Generic Error'); })); it('should decode encoded URI', () => { diff --git a/projects/aca-content/src/lib/components/search/search-results/search-results.component.ts b/projects/aca-content/src/lib/components/search/search-results/search-results.component.ts index b5d617307..e2fdc9496 100644 --- a/projects/aca-content/src/lib/components/search/search-results/search-results.component.ts +++ b/projects/aca-content/src/lib/components/search/search-results/search-results.component.ts @@ -22,7 +22,7 @@ * from Hyland Software. If not, see . */ -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, inject, OnInit, ViewEncapsulation } from '@angular/core'; import { NodeEntry, Pagination, ResultSetPaging } from '@alfresco/js-api'; import { ActivatedRoute, Params } from '@angular/router'; import { AlfrescoViewerComponent, DocumentListModule, SearchModule, SearchQueryBuilderService, TagService } from '@alfresco/adf-content-services'; @@ -31,10 +31,9 @@ import { NavigateToFolder, SetInfoDrawerPreviewStateAction, SetInfoDrawerStateAction, - ShowInfoDrawerPreviewAction, - SnackbarErrorAction + ShowInfoDrawerPreviewAction } from '@alfresco/aca-shared/store'; -import { DataTableModule, PaginationComponent, TranslationService, ViewerModule } from '@alfresco/adf-core'; +import { DataTableModule, NotificationService, PaginationComponent, TranslationService, ViewerModule } from '@alfresco/adf-core'; import { combineLatest } from 'rxjs'; import { ContextActionsDirective, @@ -94,6 +93,8 @@ import { DocumentListPresetRef, DynamicColumnComponent } from '@alfresco/adf-ext styleUrls: ['./search-results.component.scss'] }) export class SearchResultsComponent extends PageComponent implements OnInit { + private notificationService = inject(NotificationService); + infoDrawerPreview$ = this.store.select(infoDrawerPreview); searchedWord: string; @@ -180,13 +181,13 @@ export class SearchResultsComponent extends PageComponent implements OnInit { const { statusCode } = JSON.parse(error.message).error; const messageKey = `APP.BROWSE.SEARCH.ERRORS.${statusCode}`; - let translated = this.translationService.instant(messageKey); + let message = this.translationService.instant(messageKey); - if (translated === messageKey) { - translated = this.translationService.instant(`APP.BROWSE.SEARCH.ERRORS.GENERIC`); + if (message === messageKey) { + message = this.translationService.instant(`APP.BROWSE.SEARCH.ERRORS.GENERIC`); } - this.store.dispatch(new SnackbarErrorAction(translated)); + this.notificationService.showError(message); } private isOperator(input: string): boolean { diff --git a/projects/aca-content/src/lib/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.spec.ts b/projects/aca-content/src/lib/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.spec.ts index 664eff240..00a8b6afc 100644 --- a/projects/aca-content/src/lib/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.spec.ts +++ b/projects/aca-content/src/lib/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.spec.ts @@ -27,9 +27,11 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { of } from 'rxjs'; import { Store } from '@ngrx/store'; import { NodeEntry } from '@alfresco/js-api'; -import { DownloadNodesAction, EditOfflineAction, SnackbarErrorAction } from '@alfresco/aca-shared/store'; +import { DownloadNodesAction, EditOfflineAction } from '@alfresco/aca-shared/store'; import { AppTestingModule } from '../../../testing/app-testing.module'; import { AppExtensionService } from '@alfresco/aca-shared'; +import { NotificationService } from '@alfresco/adf-core'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; describe('ToggleEditOfflineComponent', () => { let fixture: ComponentFixture; @@ -38,6 +40,7 @@ describe('ToggleEditOfflineComponent', () => { let dispatchSpy: jasmine.Spy; let selectSpy: jasmine.Spy; let selection: any; + let showErrorSpy: jasmine.Spy; const extensionsMock = { updateSidebarActions: jasmine.createSpy('updateSidebarActions') @@ -45,7 +48,7 @@ describe('ToggleEditOfflineComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [AppTestingModule, ToggleEditOfflineComponent], + imports: [AppTestingModule, ToggleEditOfflineComponent, MatSnackBarModule], providers: [ { provide: Store, @@ -71,6 +74,9 @@ describe('ToggleEditOfflineComponent', () => { selectSpy = spyOn(store, 'select'); selection = { file: { entry: { name: 'test', properties: {}, isLocked: false } } }; + + const notificationService = TestBed.inject(NotificationService); + showErrorSpy = spyOn(notificationService, 'showError'); }); it('should initialized with data from store', () => { @@ -122,11 +128,7 @@ describe('ToggleEditOfflineComponent', () => { component.onLockError(); fixture.detectChanges(); - expect(dispatchSpy.calls.argsFor(0)).toEqual([ - new SnackbarErrorAction('APP.MESSAGES.ERRORS.LOCK_NODE', { - fileName: 'test' - }) - ]); + expect(showErrorSpy).toHaveBeenCalledWith('APP.MESSAGES.ERRORS.LOCK_NODE', null, { fileName: 'test' }); }); it('should raise notification on unlock error', () => { @@ -136,11 +138,7 @@ describe('ToggleEditOfflineComponent', () => { component.onUnlockError(); fixture.detectChanges(); - expect(dispatchSpy.calls.argsFor(0)).toEqual([ - new SnackbarErrorAction('APP.MESSAGES.ERRORS.UNLOCK_NODE', { - fileName: 'test' - }) - ]); + expect(showErrorSpy).toHaveBeenCalledWith('APP.MESSAGES.ERRORS.UNLOCK_NODE', null, { fileName: 'test' }); }); it('should call updateSidebarActions on click', async () => { diff --git a/projects/aca-content/src/lib/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.ts b/projects/aca-content/src/lib/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.ts index 3cae2299c..63692fbc8 100644 --- a/projects/aca-content/src/lib/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.ts +++ b/projects/aca-content/src/lib/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.ts @@ -22,19 +22,12 @@ * from Hyland Software. If not, see . */ -import { - AppStore, - DownloadNodesAction, - EditOfflineAction, - SetSelectedNodesAction, - SnackbarErrorAction, - getAppSelection -} from '@alfresco/aca-shared/store'; +import { AppStore, DownloadNodesAction, EditOfflineAction, SetSelectedNodesAction, getAppSelection } from '@alfresco/aca-shared/store'; import { NodeEntry, SharedLinkEntry, Node, NodesApi } from '@alfresco/js-api'; -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, inject, OnInit, ViewEncapsulation } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppExtensionService, isLocked } from '@alfresco/aca-shared'; -import { AlfrescoApiService } from '@alfresco/adf-core'; +import { AlfrescoApiService, NotificationService } from '@alfresco/adf-core'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; import { MatMenuModule } from '@angular/material/menu'; @@ -54,6 +47,8 @@ import { MatIconModule } from '@angular/material/icon'; host: { class: 'app-toggle-edit-offline' } }) export class ToggleEditOfflineComponent implements OnInit { + private notificationService = inject(NotificationService); + private nodesApi: NodesApi; selection: NodeEntry; nodeTitle = ''; @@ -104,19 +99,11 @@ export class ToggleEditOfflineComponent implements OnInit { } onLockError() { - this.store.dispatch( - new SnackbarErrorAction('APP.MESSAGES.ERRORS.LOCK_NODE', { - fileName: this.selection.entry.name - }) - ); + this.notificationService.showError('APP.MESSAGES.ERRORS.LOCK_NODE', null, { fileName: this.selection.entry.name }); } onUnlockError() { - this.store.dispatch( - new SnackbarErrorAction('APP.MESSAGES.ERRORS.UNLOCK_NODE', { - fileName: this.selection.entry.name - }) - ); + this.notificationService.showError('APP.MESSAGES.ERRORS.UNLOCK_NODE', null, { fileName: this.selection.entry.name }); } lockNode(nodeId: string) { diff --git a/projects/aca-content/src/lib/components/toolbar/toggle-join-library/toggle-join-library-button.component.ts b/projects/aca-content/src/lib/components/toolbar/toggle-join-library/toggle-join-library-button.component.ts index 220599163..f6ba10d17 100644 --- a/projects/aca-content/src/lib/components/toolbar/toggle-join-library/toggle-join-library-button.component.ts +++ b/projects/aca-content/src/lib/components/toolbar/toggle-join-library/toggle-join-library-button.component.ts @@ -22,7 +22,7 @@ * from Hyland Software. If not, see . */ -import { AppStore, SetSelectedNodesAction, SnackbarErrorAction, SnackbarInfoAction, getAppSelection } from '@alfresco/aca-shared/store'; +import { AppStore, SetSelectedNodesAction, getAppSelection } from '@alfresco/aca-shared/store'; import { AppHookService, UserProfileService } from '@alfresco/aca-shared'; import { SelectionState } from '@alfresco/adf-extensions'; import { Component, inject, ViewEncapsulation } from '@angular/core'; @@ -33,6 +33,7 @@ import { CommonModule } from '@angular/common'; import { MatButtonModule } from '@angular/material/button'; import { TranslateModule } from '@ngx-translate/core'; import { MatIconModule } from '@angular/material/icon'; +import { NotificationService } from '@alfresco/adf-core'; @Component({ standalone: true, @@ -58,16 +59,19 @@ import { MatIconModule } from '@angular/material/icon'; }) export class ToggleJoinLibraryButtonComponent { private userProfileService = inject(UserProfileService); + private notificationService = inject(NotificationService); + private appHookService = inject(AppHookService); + private store = inject(Store); selection$: Observable; profile$ = this.userProfileService.userProfile$; - constructor(private store: Store, private appHookService: AppHookService) { + constructor() { this.selection$ = this.store.select(getAppSelection); } onToggleEvent(event: LibraryMembershipToggleEvent) { - this.store.dispatch(new SnackbarInfoAction(event.i18nKey)); + this.notificationService.showInfo(event.i18nKey); if (event.shouldReload) { this.appHookService.libraryJoined.next(); @@ -80,6 +84,6 @@ export class ToggleJoinLibraryButtonComponent { } onErrorEvent(event: LibraryMembershipErrorEvent) { - this.store.dispatch(new SnackbarErrorAction(event.i18nKey)); + this.notificationService.showError(event.i18nKey); } } diff --git a/projects/aca-content/src/lib/components/toolbar/toggle-join-library/toggle-join-library-menu.component.ts b/projects/aca-content/src/lib/components/toolbar/toggle-join-library/toggle-join-library-menu.component.ts index 62391af71..cf20fd5bc 100644 --- a/projects/aca-content/src/lib/components/toolbar/toggle-join-library/toggle-join-library-menu.component.ts +++ b/projects/aca-content/src/lib/components/toolbar/toggle-join-library/toggle-join-library-menu.component.ts @@ -23,9 +23,6 @@ */ import { Component, ViewEncapsulation } from '@angular/core'; -import { Store } from '@ngrx/store'; -import { AppHookService } from '@alfresco/aca-shared'; -import { AppStore } from '@alfresco/aca-shared/store'; import { ToggleJoinLibraryButtonComponent } from './toggle-join-library-button.component'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; @@ -55,8 +52,4 @@ import { MatMenuModule } from '@angular/material/menu'; encapsulation: ViewEncapsulation.None, host: { class: 'app-toggle-join-library' } }) -export class ToggleJoinLibraryMenuComponent extends ToggleJoinLibraryButtonComponent { - constructor(store: Store, appHookService: AppHookService) { - super(store, appHookService); - } -} +export class ToggleJoinLibraryMenuComponent extends ToggleJoinLibraryButtonComponent {} diff --git a/projects/aca-content/src/lib/components/toolbar/toggle-join-library/toggle-join-library.component.spec.ts b/projects/aca-content/src/lib/components/toolbar/toggle-join-library/toggle-join-library.component.spec.ts index 083105fca..d1e489417 100644 --- a/projects/aca-content/src/lib/components/toolbar/toggle-join-library/toggle-join-library.component.spec.ts +++ b/projects/aca-content/src/lib/components/toolbar/toggle-join-library/toggle-join-library.component.spec.ts @@ -26,18 +26,20 @@ import { of } from 'rxjs'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { Store } from '@ngrx/store'; import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { SnackbarErrorAction, SnackbarInfoAction } from '@alfresco/aca-shared/store'; import { AppTestingModule } from '../../../testing/app-testing.module'; import { ToggleJoinLibraryButtonComponent } from './toggle-join-library-button.component'; import { AppHookService, ContentApiService } from '@alfresco/aca-shared'; +import { NotificationService } from '@alfresco/adf-core'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; describe('ToggleJoinLibraryComponent', () => { let component: ToggleJoinLibraryButtonComponent; let fixture: ComponentFixture; let appHookService: AppHookService; let contentApiService: any; - let store: Store; let entry; + let showErrorSpy: jasmine.Spy; + let showInfoSpy: jasmine.Spy; beforeEach(() => { entry = { @@ -48,7 +50,7 @@ describe('ToggleJoinLibraryComponent', () => { }; TestBed.configureTestingModule({ - imports: [AppTestingModule, ToggleJoinLibraryButtonComponent], + imports: [AppTestingModule, ToggleJoinLibraryButtonComponent, MatSnackBarModule], providers: [ { provide: Store, @@ -61,9 +63,12 @@ describe('ToggleJoinLibraryComponent', () => { schemas: [NO_ERRORS_SCHEMA] }); - store = TestBed.inject(Store); appHookService = TestBed.inject(AppHookService); + const notificationService = TestBed.inject(NotificationService); + showErrorSpy = spyOn(notificationService, 'showError'); + showInfoSpy = spyOn(notificationService, 'showInfo'); + contentApiService = TestBed.inject(ContentApiService); fixture = TestBed.createComponent(ToggleJoinLibraryButtonComponent); component = fixture.componentInstance; @@ -81,18 +86,18 @@ describe('ToggleJoinLibraryComponent', () => { }); }); - it('should dispatch `SnackbarErrorAction` action on error', () => { + it('should show error notification on error', () => { const event = { error: {}, i18nKey: 'ERROR_i18nKey' }; component.onErrorEvent(event); - expect(store.dispatch).toHaveBeenCalledWith(new SnackbarErrorAction(event.i18nKey)); + expect(showErrorSpy).toHaveBeenCalledWith(event.i18nKey); }); - it('should dispatch `SnackbarInfoAction` action on onToggleEvent', () => { + it('should show info notification on onToggleEvent', () => { const event = { shouldReload: true, i18nKey: 'SOME_i18nKey' }; component.onToggleEvent(event); - expect(store.dispatch).toHaveBeenCalledWith(new SnackbarInfoAction(event.i18nKey)); + expect(showInfoSpy).toHaveBeenCalledWith(event.i18nKey); }); it('should call libraryJoined.next on contentManagementService onToggleEvent', (done) => { diff --git a/projects/aca-content/src/lib/services/content-management.service.spec.ts b/projects/aca-content/src/lib/services/content-management.service.spec.ts index 2f9e5e1a9..dcfa4487c 100644 --- a/projects/aca-content/src/lib/services/content-management.service.spec.ts +++ b/projects/aca-content/src/lib/services/content-management.service.spec.ts @@ -79,6 +79,9 @@ describe('ContentManagementService', () => { let nodeAspectService: NodeAspectService; let appHookService: AppHookService; let newVersionUploaderService: NewVersionUploaderService; + let showErrorSpy: jasmine.Spy; + let showInfoSpy: jasmine.Spy; + let showWarningSpy: jasmine.Spy; beforeEach(() => { TestBed.configureTestingModule({ @@ -90,6 +93,9 @@ describe('ContentManagementService', () => { store = TestBed.inject(Store); contentManagementService = TestBed.inject(ContentManagementService); notificationService = TestBed.inject(NotificationService); + showErrorSpy = spyOn(notificationService, 'showError'); + showInfoSpy = spyOn(notificationService, 'showInfo'); + showWarningSpy = spyOn(notificationService, 'showWarning'); nodeActions = TestBed.inject(NodeActionsService); translationService = TestBed.inject(TranslationService); nodesApiService = TestBed.inject(NodesApiService); @@ -923,14 +929,7 @@ describe('ContentManagementService', () => { }); describe('notification', () => { - it('raises warning on multiple fail and one success', (done) => { - actions$ - .pipe( - ofType(SnackbarActionTypes.Warning), - map((action) => expect(action).toBeDefined()) - ) - .subscribe(() => done()); - + it('raises warning on multiple fail and one success', () => { spyOn(contentApi, 'purgeDeletedNode').and.callFake((id) => { if (id === '1') { return of({}); @@ -954,16 +953,10 @@ describe('ContentManagementService', () => { ]; store.dispatch(new PurgeDeletedNodesAction(selection)); + expect(showWarningSpy).toHaveBeenCalled(); }); - it('raises warning on multiple success and multiple fail', (done) => { - actions$ - .pipe( - ofType(SnackbarActionTypes.Warning), - map((action) => expect(action).toBeDefined()) - ) - .subscribe(() => done()); - + it('raises warning on multiple success and multiple fail', () => { spyOn(contentApi, 'purgeDeletedNode').and.callFake((id) => { if (id === '1') { return of({}); @@ -992,46 +985,28 @@ describe('ContentManagementService', () => { ]; store.dispatch(new PurgeDeletedNodesAction(selection)); + expect(showWarningSpy).toHaveBeenCalled(); }); - it('raises info on one selected node success', (done) => { - actions$ - .pipe( - ofType(SnackbarActionTypes.Info), - map((action) => expect(action).toBeDefined()) - ) - .subscribe(() => done()); - + it('raises info on one selected node success', () => { spyOn(contentApi, 'purgeDeletedNode').and.returnValue(of({})); const selection: any[] = [{ entry: { id: '1', name: 'name1' } }]; store.dispatch(new PurgeDeletedNodesAction(selection)); + expect(showInfoSpy).toHaveBeenCalled(); }); - it('raises error on one selected node fail', (done) => { - actions$ - .pipe( - ofType(SnackbarActionTypes.Error), - map((action) => expect(action).toBeDefined()) - ) - .subscribe(() => done()); - + it('raises error on one selected node fail', () => { spyOn(contentApi, 'purgeDeletedNode').and.returnValue(throwError({})); const selection: any[] = [{ entry: { id: '1', name: 'name1' } }]; store.dispatch(new PurgeDeletedNodesAction(selection)); + expect(showErrorSpy).toHaveBeenCalled(); }); - it('raises info on all nodes success', (done) => { - actions$ - .pipe( - ofType(SnackbarActionTypes.Info), - map((action) => expect(action).toBeDefined()) - ) - .subscribe(() => done()); - + it('raises info on all nodes success', () => { spyOn(contentApi, 'purgeDeletedNode').and.callFake((id) => { if (id === '1') { return of({}); @@ -1047,16 +1022,10 @@ describe('ContentManagementService', () => { const selection: any[] = [{ entry: { id: '1', name: 'name1' } }, { entry: { id: '2', name: 'name2' } }]; store.dispatch(new PurgeDeletedNodesAction(selection)); + expect(showInfoSpy).toHaveBeenCalled(); }); - it('raises error on all nodes fail', (done) => { - actions$ - .pipe( - ofType(SnackbarActionTypes.Error), - map((action) => expect(action).toBeDefined()) - ) - .subscribe(() => done()); - + it('raises error on all nodes fail', () => { spyOn(contentApi, 'purgeDeletedNode').and.callFake((id) => { if (id === '1') { return throwError({}); @@ -1072,6 +1041,7 @@ describe('ContentManagementService', () => { const selection: any[] = [{ entry: { id: '1', name: 'name1' } }, { entry: { id: '2', name: 'name2' } }]; store.dispatch(new PurgeDeletedNodesAction(selection)); + expect(showErrorSpy).toHaveBeenCalled(); }); }); }); @@ -1510,11 +1480,7 @@ describe('ContentManagementService', () => { tick(); flush(); - expect(store.dispatch['calls'].argsFor(1)[0]).toEqual( - new SnackbarErrorAction('APP.MESSAGES.ERRORS.UNLOCK_NODE', { - fileName: 'some-file' - }) - ); + expect(showErrorSpy).toHaveBeenCalledWith('APP.MESSAGES.ERRORS.UNLOCK_NODE', null, { fileName: 'some-file' }); })); }); @@ -1573,7 +1539,8 @@ describe('ContentManagementService', () => { const fakeError = 'Upload error'; spyOnOpenUploadNewVersionDialog.and.returnValue(throwError(fakeError)); contentManagementService.versionUpdateDialog(fakeNode, fakeFile); - expect(spyOnDispatch).toHaveBeenCalledOnceWith(new SnackbarErrorAction(fakeError)); + + expect(showErrorSpy).toHaveBeenCalledOnceWith(fakeError); }); }); @@ -1627,7 +1594,7 @@ describe('ContentManagementService', () => { it('should show permission error is node is not a file and does not have nodeId', () => { contentManagementService.manageVersions(fakeNodeIsNotFile); - expect(spyOnDispatch).toHaveBeenCalledOnceWith(new SnackbarErrorAction('APP.MESSAGES.ERRORS.PERMISSION')); + expect(showErrorSpy).toHaveBeenCalledWith('APP.MESSAGES.ERRORS.PERMISSION'); }); }); @@ -1658,7 +1625,7 @@ describe('ContentManagementService', () => { mockDialogInstance.componentInstance.error.next('edit folder error'); - expect(store.dispatch['calls'].argsFor(0)[0]).toEqual(new SnackbarErrorAction('edit folder error')); + expect(showErrorSpy).toHaveBeenCalledWith('edit folder error'); })); it('should call nodeUpdated event with edited node data', fakeAsync(() => { @@ -1783,7 +1750,7 @@ describe('ContentManagementService', () => { contentManagementService.deleteLibrary(libraryId); tick(); - expect(store.dispatch).toHaveBeenCalledWith(new SnackbarInfoAction('APP.MESSAGES.INFO.LIBRARY_DELETED')); + expect(showInfoSpy).toHaveBeenCalledWith('APP.MESSAGES.INFO.LIBRARY_DELETED'); expect(store.dispatch).toHaveBeenCalledWith(new NavigateRouteAction(['/libraries'])); })); }); diff --git a/projects/aca-content/src/lib/services/content-management.service.ts b/projects/aca-content/src/lib/services/content-management.service.ts index d51ef50ba..0fa96a0d7 100644 --- a/projects/aca-content/src/lib/services/content-management.service.ts +++ b/projects/aca-content/src/lib/services/content-management.service.ts @@ -57,7 +57,7 @@ import { } from '@alfresco/adf-content-services'; import { NotificationService, TranslationService, ConfirmDialogComponent } from '@alfresco/adf-core'; import { DeletedNodesPaging, Node, NodeEntry, PathInfo, SiteBodyCreate, SiteEntry } from '@alfresco/js-api'; -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; import { forkJoin, Observable, of, zip } from 'rxjs'; @@ -75,6 +75,7 @@ interface RestoredNode { providedIn: 'root' }) export class ContentManagementService { + private notificationService = inject(NotificationService); private readonly createMenuButtonSelector = 'app-toolbar-menu button[id="app.toolbar.create"]'; constructor( @@ -85,7 +86,6 @@ export class ContentManagementService { private dialogRef: MatDialog, private nodeActionsService: NodeActionsService, private translation: TranslationService, - private notificationService: NotificationService, private nodeAspectService: NodeAspectService, private appHookService: AppHookService, private newVersionUploaderService: NewVersionUploaderService, @@ -168,7 +168,7 @@ export class ContentManagementService { } } }, - (error) => this.store.dispatch(new SnackbarErrorAction(error)) + (error) => this.notificationService.showError(error) ); }); } @@ -221,7 +221,7 @@ export class ContentManagementService { }); dialogInstance.componentInstance.error.subscribe((message: string) => { - this.store.dispatch(new SnackbarErrorAction(message)); + this.notificationService.showError(message); }); dialogInstance.afterClosed().subscribe((node) => { @@ -245,7 +245,7 @@ export class ContentManagementService { }); dialog.componentInstance.error.subscribe((message: string) => { - this.store.dispatch(new SnackbarErrorAction(message)); + this.notificationService.showError(message); }); dialog.afterClosed().subscribe((node) => { @@ -263,7 +263,7 @@ export class ContentManagementService { }); dialogInstance.componentInstance.error.subscribe((message: string) => { - this.store.dispatch(new SnackbarErrorAction(message)); + this.notificationService.showError(message); }); return dialogInstance.afterClosed().pipe( @@ -286,11 +286,11 @@ export class ContentManagementService { this.contentApi.deleteSite(id).subscribe( () => { this.appHookService.libraryDeleted.next(id); - this.store.dispatch(new SnackbarInfoAction('APP.MESSAGES.INFO.LIBRARY_DELETED')); + this.notificationService.showInfo('APP.MESSAGES.INFO.LIBRARY_DELETED'); this.store.dispatch(new NavigateRouteAction(['/libraries'])); }, () => { - this.store.dispatch(new SnackbarErrorAction('APP.MESSAGES.ERRORS.DELETE_LIBRARY_FAILED')); + this.notificationService.showError('APP.MESSAGES.ERRORS.DELETE_LIBRARY_FAILED'); } ); } @@ -311,10 +311,10 @@ export class ContentManagementService { this.contentApi.leaveSite(siteId).subscribe( () => { this.appHookService.libraryLeft.next(siteId); - this.store.dispatch(new SnackbarInfoAction('APP.MESSAGES.INFO.LEFT_LIBRARY')); + this.notificationService.showInfo('APP.MESSAGES.INFO.LEFT_LIBRARY'); }, () => { - this.store.dispatch(new SnackbarErrorAction('APP.MESSAGES.ERRORS.LEAVE_LIBRARY_FAILED')); + this.notificationService.showError('APP.MESSAGES.ERRORS.LEAVE_LIBRARY_FAILED'); } ); } @@ -326,10 +326,10 @@ export class ContentManagementService { this.contentApi.updateLibrary(siteId, siteBody).subscribe( (siteEntry: SiteEntry) => { this.appHookService.libraryUpdated.next(siteEntry); - this.store.dispatch(new SnackbarInfoAction('LIBRARY.SUCCESS.LIBRARY_UPDATED')); + this.notificationService.showInfo('LIBRARY.SUCCESS.LIBRARY_UPDATED'); }, () => { - this.store.dispatch(new SnackbarErrorAction('LIBRARY.ERRORS.LIBRARY_UPDATE_ERROR')); + this.notificationService.showError('LIBRARY.ERRORS.LIBRARY_UPDATE_ERROR'); } ); } @@ -480,11 +480,7 @@ export class ContentManagementService { unlockNode(node: NodeEntry): Promise { return this.contentApi.unlockNode(node.entry.id).catch(() => { - this.store.dispatch( - new SnackbarErrorAction('APP.MESSAGES.ERRORS.UNLOCK_NODE', { - fileName: node.entry.name - }) - ); + this.notificationService.showError('APP.MESSAGES.ERRORS.UNLOCK_NODE', null, { fileName: node.entry.name }); }); } @@ -560,7 +556,7 @@ export class ContentManagementService { i18nMessageString = 'APP.MESSAGES.ERRORS.PERMISSION'; } - this.store.dispatch(new SnackbarErrorAction(i18nMessageString)); + this.notificationService.showError(i18nMessageString); } ); } @@ -594,7 +590,7 @@ export class ContentManagementService { } }); } else { - this.store.dispatch(new SnackbarErrorAction('APP.MESSAGES.ERRORS.PERMISSION')); + this.notificationService.showError('APP.MESSAGES.ERRORS.PERMISSION'); } } @@ -603,7 +599,7 @@ export class ContentManagementService { if (node.isFile || node.id) { this.nodeAspectService.updateNodeAspects(node.id, focusedElementOnCloseSelector); } else { - this.store.dispatch(new SnackbarErrorAction('APP.MESSAGES.ERRORS.PERMISSION')); + this.notificationService.showError('APP.MESSAGES.ERRORS.PERMISSION'); } } @@ -650,7 +646,7 @@ export class ContentManagementService { message = 'APP.MESSAGES.ERRORS.PERMISSION'; } - this.store.dispatch(new SnackbarErrorAction(message)); + this.notificationService.showError(message); } ); } @@ -767,10 +763,8 @@ export class ContentManagementService { if (status.success.length) { this.store.dispatch(new ReloadDocumentListAction()); } - const message = this.getPurgeMessage(status); - if (message) { - this.store.dispatch(message); - } + + this.sendPurgeMessage(status); }); } @@ -832,38 +826,42 @@ export class ContentManagementService { }, status); } - private getPurgeMessage(status: DeleteStatus): SnackbarAction { + private sendPurgeMessage(status: DeleteStatus): void { if (status.oneSucceeded && status.someFailed && !status.oneFailed) { - return new SnackbarWarningAction('APP.MESSAGES.INFO.TRASH.NODES_PURGE.PARTIAL_SINGULAR', { + this.notificationService.showWarning('APP.MESSAGES.INFO.TRASH.NODES_PURGE.PARTIAL_SINGULAR', null, { name: status.success[0].name, failed: status.fail.length }); + return; } if (status.someSucceeded && !status.oneSucceeded && status.someFailed) { - return new SnackbarWarningAction('APP.MESSAGES.INFO.TRASH.NODES_PURGE.PARTIAL_PLURAL', { + this.notificationService.showWarning('APP.MESSAGES.INFO.TRASH.NODES_PURGE.PARTIAL_PLURAL', null, { number: status.success.length, failed: status.fail.length }); + return; } if (status.oneSucceeded) { - return new SnackbarInfoAction('APP.MESSAGES.INFO.TRASH.NODES_PURGE.SINGULAR', { name: status.success[0].name }); + this.notificationService.showInfo('APP.MESSAGES.INFO.TRASH.NODES_PURGE.SINGULAR', null, { name: status.success[0].name }); + return; } if (status.oneFailed) { - return new SnackbarErrorAction('APP.MESSAGES.ERRORS.TRASH.NODES_PURGE.SINGULAR', { name: status.fail[0].name }); + this.notificationService.showError('APP.MESSAGES.ERRORS.TRASH.NODES_PURGE.SINGULAR', null, { name: status.fail[0].name }); + return; } if (status.allSucceeded) { - return new SnackbarInfoAction('APP.MESSAGES.INFO.TRASH.NODES_PURGE.PLURAL', { number: status.success.length }); + this.notificationService.showInfo('APP.MESSAGES.INFO.TRASH.NODES_PURGE.PLURAL', null, { number: status.success.length }); + return; } if (status.allFailed) { - return new SnackbarErrorAction('APP.MESSAGES.ERRORS.TRASH.NODES_PURGE.PLURAL', { number: status.fail.length }); + this.notificationService.showError('APP.MESSAGES.ERRORS.TRASH.NODES_PURGE.PLURAL', null, { number: status.fail.length }); + return; } - - return null; } private showRestoreNotification(status: DeleteStatus): void { diff --git a/projects/aca-content/src/lib/services/node-template.service.spec.ts b/projects/aca-content/src/lib/services/node-template.service.spec.ts index 0eb8add4f..9c0add2db 100644 --- a/projects/aca-content/src/lib/services/node-template.service.spec.ts +++ b/projects/aca-content/src/lib/services/node-template.service.spec.ts @@ -24,18 +24,21 @@ import { TestBed, fakeAsync, tick } from '@angular/core/testing'; import { EffectsModule } from '@ngrx/effects'; -import { AppStore, SnackbarErrorAction } from '@alfresco/aca-shared/store'; import { TemplateEffects } from '../store/effects/template.effects'; import { AppTestingModule } from '../testing/app-testing.module'; import { Store } from '@ngrx/store'; import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { NodeTemplateService } from './node-template.service'; import { ResultSetPaging } from '@alfresco/js-api'; +import { NotificationService } from '@alfresco/adf-core'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; describe('NodeTemplateService', () => { let dialog: MatDialog; - let store: Store; + let store: Store; let nodeTemplateService: NodeTemplateService; + let showErrorSpy: jasmine.Spy; + const fileTemplateConfig = { primaryPathName: 'parent-file-templates', selectionType: 'file' @@ -47,7 +50,7 @@ describe('NodeTemplateService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [AppTestingModule, EffectsModule.forRoot([TemplateEffects]), MatDialogModule], + imports: [AppTestingModule, EffectsModule.forRoot([TemplateEffects]), MatDialogModule, MatSnackBarModule], providers: [NodeTemplateService] }); @@ -55,10 +58,13 @@ describe('NodeTemplateService', () => { dialog = TestBed.inject(MatDialog); nodeTemplateService = TestBed.inject(NodeTemplateService); spyOn(document, 'querySelector').and.returnValue(document.createElement('button')); + + const notificationService = TestBed.inject(NotificationService); + showErrorSpy = spyOn(notificationService, 'showError'); }); it('should open dialog with parent node `id` as data property', fakeAsync(() => { - spyOn(nodeTemplateService['searchApi'], 'search').and.returnValue( + spyOn(nodeTemplateService.searchApi, 'search').and.returnValue( Promise.resolve({ list: { entries: [{ entry: { id: 'parent-node-id' } }] } } as ResultSetPaging) @@ -72,7 +78,7 @@ describe('NodeTemplateService', () => { })); it('should remove parents path for templates breadcrumb', fakeAsync(() => { - spyOn(nodeTemplateService['searchApi'], 'search').and.returnValue( + spyOn(nodeTemplateService.searchApi, 'search').and.returnValue( Promise.resolve({ list: { entries: [ @@ -112,7 +118,7 @@ describe('NodeTemplateService', () => { })); it('should set template folder path as root for breadcrumb', fakeAsync(() => { - spyOn(nodeTemplateService['searchApi'], 'search').and.returnValue( + spyOn(nodeTemplateService.searchApi, 'search').and.returnValue( Promise.resolve({ list: { entries: [ @@ -153,17 +159,17 @@ describe('NodeTemplateService', () => { })); it('should raise an error when getNodeInfo fails', fakeAsync(() => { - spyOn(nodeTemplateService['searchApi'], 'search').and.returnValue(Promise.reject(new Error('{ "error": { "statusCode": 404 } }'))); + spyOn(nodeTemplateService.searchApi, 'search').and.returnValue(Promise.reject(new Error('{ "error": { "statusCode": 404 } }'))); spyOn(store, 'dispatch'); nodeTemplateService.selectTemplateDialog(fileTemplateConfig); tick(); - expect(store.dispatch).toHaveBeenCalledWith(new SnackbarErrorAction('APP.MESSAGES.ERRORS.GENERIC')); + expect(showErrorSpy).toHaveBeenCalledWith('APP.MESSAGES.ERRORS.GENERIC'); })); it('should return true if row is not a `link` nodeType', fakeAsync(() => { - spyOn(nodeTemplateService['searchApi'], 'search').and.returnValue( + spyOn(nodeTemplateService.searchApi, 'search').and.returnValue( Promise.resolve({ list: { entries: [ @@ -193,7 +199,7 @@ describe('NodeTemplateService', () => { })); it('should return false if row is a `filelink` nodeType', fakeAsync(() => { - spyOn(nodeTemplateService['searchApi'], 'search').and.returnValue( + spyOn(nodeTemplateService.searchApi, 'search').and.returnValue( Promise.resolve({ list: { entries: [ @@ -223,7 +229,7 @@ describe('NodeTemplateService', () => { })); it('should return false if row is a `folderlink` nodeType', fakeAsync(() => { - spyOn(nodeTemplateService['searchApi'], 'search').and.returnValue( + spyOn(nodeTemplateService.searchApi, 'search').and.returnValue( Promise.resolve({ list: { entries: [ @@ -254,7 +260,7 @@ describe('NodeTemplateService', () => { describe('File templates', () => { it('should return false if selected node is not a file', fakeAsync(() => { - spyOn(nodeTemplateService['searchApi'], 'search').and.returnValue( + spyOn(nodeTemplateService.searchApi, 'search').and.returnValue( Promise.resolve({ list: { entries: [{ entry: { id: 'templates-folder-id' } }] } } as ResultSetPaging) @@ -275,7 +281,7 @@ describe('NodeTemplateService', () => { })); it('should return true if selected node is a template file', fakeAsync(() => { - spyOn(nodeTemplateService['searchApi'], 'search').and.returnValue( + spyOn(nodeTemplateService.searchApi, 'search').and.returnValue( Promise.resolve({ list: { entries: [{ entry: { id: 'templates-folder-id' } }] } } as ResultSetPaging) @@ -296,7 +302,7 @@ describe('NodeTemplateService', () => { })); it('should set dialog title for file templates', fakeAsync(() => { - spyOn(nodeTemplateService['searchApi'], 'search').and.returnValue( + spyOn(nodeTemplateService.searchApi, 'search').and.returnValue( Promise.resolve({ list: { entries: [{ entry: { id: 'templates-folder-id' } }] } } as ResultSetPaging) @@ -314,7 +320,7 @@ describe('NodeTemplateService', () => { describe('Folder templates', () => { it('should return false if selected node is not a folder', fakeAsync(() => { - spyOn(nodeTemplateService['searchApi'], 'search').and.returnValue( + spyOn(nodeTemplateService.searchApi, 'search').and.returnValue( Promise.resolve({ list: { entries: [{ entry: { id: 'templates-folder-id' } }] } } as ResultSetPaging) @@ -335,7 +341,7 @@ describe('NodeTemplateService', () => { })); it('should return false if current node is the parent folder', fakeAsync(() => { - spyOn(nodeTemplateService['searchApi'], 'search').and.returnValue( + spyOn(nodeTemplateService.searchApi, 'search').and.returnValue( Promise.resolve({ list: { entries: [{ entry: { id: 'templates-folder-id' } }] } } as ResultSetPaging) @@ -356,7 +362,7 @@ describe('NodeTemplateService', () => { })); it('should return true if selected node is a folder template', fakeAsync(() => { - spyOn(nodeTemplateService['searchApi'], 'search').and.returnValue( + spyOn(nodeTemplateService.searchApi, 'search').and.returnValue( Promise.resolve({ list: { entries: [{ entry: { id: 'templates-folder-id', path: { elements: [] } } }] @@ -379,7 +385,7 @@ describe('NodeTemplateService', () => { })); it('should set dialog title for folder templates', fakeAsync(() => { - spyOn(nodeTemplateService['searchApi'], 'search').and.returnValue( + spyOn(nodeTemplateService.searchApi, 'search').and.returnValue( Promise.resolve({ list: { entries: [{ entry: { id: 'templates-folder-id' } }] } } as ResultSetPaging) diff --git a/projects/aca-content/src/lib/services/node-template.service.ts b/projects/aca-content/src/lib/services/node-template.service.ts index d40d4555e..41c2f3507 100644 --- a/projects/aca-content/src/lib/services/node-template.service.ts +++ b/projects/aca-content/src/lib/services/node-template.service.ts @@ -22,15 +22,13 @@ * from Hyland Software. If not, see . */ -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { MatDialog, MatDialogRef } from '@angular/material/dialog'; import { CreateFromTemplateDialogComponent } from '../dialogs/node-template/create-from-template.dialog'; import { Subject, from, of } from 'rxjs'; import { Node, ResultNode, PathElement, SearchApi } from '@alfresco/js-api'; -import { AlfrescoApiService, TranslationService } from '@alfresco/adf-core'; +import { AlfrescoApiService, TranslationService, NotificationService } from '@alfresco/adf-core'; import { switchMap, catchError } from 'rxjs/operators'; -import { Store } from '@ngrx/store'; -import { AppStore, SnackbarErrorAction } from '@alfresco/aca-shared/store'; import { ContentNodeSelectorComponent, ContentNodeSelectorComponentData, ShareDataRow, NodeAction } from '@alfresco/adf-content-services'; export interface TemplateDialogConfig { @@ -42,6 +40,11 @@ export interface TemplateDialogConfig { providedIn: 'root' }) export class NodeTemplateService { + private alfrescoApiService = inject(AlfrescoApiService); + private notificationService = inject(NotificationService); + private translation = inject(TranslationService); + private dialog = inject(MatDialog); + private currentTemplateConfig: TemplateDialogConfig = null; private rootNode: ResultNode; @@ -51,13 +54,6 @@ export class NodeTemplateService { return this._searchApi; } - constructor( - private store: Store, - private alfrescoApiService: AlfrescoApiService, - private translation: TranslationService, - public dialog: MatDialog - ) {} - selectTemplateDialog(config: TemplateDialogConfig): Subject { this.currentTemplateConfig = config; @@ -105,7 +101,7 @@ export class NodeTemplateService { .afterClosed(); }), catchError((error) => { - this.store.dispatch(new SnackbarErrorAction('APP.MESSAGES.ERRORS.GENERIC')); + this.notificationService.showError('APP.MESSAGES.ERRORS.GENERIC'); return of(error); }) ) @@ -120,7 +116,7 @@ export class NodeTemplateService { panelClass: 'aca-create-from-template-dialog', width: '630px' }); - dialog.afterClosed().subscribe(() => NodeTemplateService.focusCreateMenuButton()); + dialog.afterClosed().subscribe(() => this.focusCreateMenuButton()); return dialog; } @@ -145,7 +141,7 @@ export class NodeTemplateService { private close() { this.dialog.closeAll(); - NodeTemplateService.focusCreateMenuButton(); + this.focusCreateMenuButton(); } private title(selectionType: string) { @@ -165,7 +161,7 @@ export class NodeTemplateService { return node.path.elements.filter((pathElement) => !this.rootNode.path.elements.some((rootPathElement) => pathElement.id === rootPathElement.id)); } - private static focusCreateMenuButton(): void { + private focusCreateMenuButton(): void { document.querySelector('app-toolbar-menu button[id="app.toolbar.create"]').focus(); } } diff --git a/projects/aca-content/src/lib/store/effects/library.effects.ts b/projects/aca-content/src/lib/store/effects/library.effects.ts index e2791b7cf..73e02a80c 100644 --- a/projects/aca-content/src/lib/store/effects/library.effects.ts +++ b/projects/aca-content/src/lib/store/effects/library.effects.ts @@ -30,19 +30,21 @@ import { LibraryActionTypes, NavigateLibraryAction, NavigateRouteAction, - SnackbarErrorAction, UpdateLibraryAction, getAppSelection } from '@alfresco/aca-shared/store'; -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { Actions, ofType, createEffect } from '@ngrx/effects'; import { Store } from '@ngrx/store'; import { map, mergeMap, take } from 'rxjs/operators'; import { ContentApiService } from '@alfresco/aca-shared'; import { ContentManagementService } from '../../services/content-management.service'; +import { NotificationService } from '@alfresco/adf-core'; @Injectable() export class LibraryEffects { + private notificationService = inject(NotificationService); + constructor( private store: Store, private actions$: Actions, @@ -120,7 +122,7 @@ export class LibraryEffects { this.store.dispatch(new NavigateRouteAction([route, id])); }, () => { - this.store.dispatch(new SnackbarErrorAction('APP.MESSAGES.ERRORS.MISSING_CONTENT')); + this.notificationService.showError('APP.MESSAGES.ERRORS.MISSING_CONTENT'); } ); } diff --git a/projects/aca-content/src/lib/store/effects/template.effects.spec.ts b/projects/aca-content/src/lib/store/effects/template.effects.spec.ts index 09b92750f..73dcac231 100644 --- a/projects/aca-content/src/lib/store/effects/template.effects.spec.ts +++ b/projects/aca-content/src/lib/store/effects/template.effects.spec.ts @@ -27,13 +27,15 @@ import { AppTestingModule } from '../../testing/app-testing.module'; import { TemplateEffects } from './template.effects'; import { EffectsModule } from '@ngrx/effects'; import { Store } from '@ngrx/store'; -import { CreateFromTemplate, CreateFromTemplateSuccess, FileFromTemplate, FolderFromTemplate, SnackbarErrorAction } from '@alfresco/aca-shared/store'; +import { CreateFromTemplate, CreateFromTemplateSuccess, FileFromTemplate, FolderFromTemplate } from '@alfresco/aca-shared/store'; import { NodeTemplateService } from '../../services/node-template.service'; import { of, Subject } from 'rxjs'; import { Node, NodeEntry } from '@alfresco/js-api'; import { MatDialog, MatDialogRef } from '@angular/material/dialog'; import { CreateFromTemplateDialogComponent } from '../../dialogs/node-template/create-from-template.dialog'; import { AppHookService } from '@alfresco/aca-shared'; +import { NotificationService } from '@alfresco/adf-core'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; describe('TemplateEffects', () => { let store: Store; @@ -43,6 +45,8 @@ describe('TemplateEffects', () => { let copyNodeSpy; let updateNodeSpy; let matDialog: MatDialog; + let showErrorSpy; + const node: Node = { name: 'node-name', id: 'node-id', @@ -72,7 +76,7 @@ describe('TemplateEffects', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [AppTestingModule, EffectsModule.forRoot([TemplateEffects])], + imports: [AppTestingModule, EffectsModule.forRoot([TemplateEffects]), MatSnackBarModule], providers: [ NodeTemplateService, { @@ -91,13 +95,16 @@ describe('TemplateEffects', () => { matDialog = TestBed.inject(MatDialog); subject = new Subject(); + const notificationService = TestBed.inject(NotificationService); + showErrorSpy = spyOn(notificationService, 'showError'); + spyOn(store, 'dispatch').and.callThrough(); spyOn(appHookService.reload, 'next'); spyOn(store, 'select').and.returnValue(of({ id: 'parent-id' })); spyOn(nodeTemplateService, 'selectTemplateDialog').and.returnValue(subject); - copyNodeSpy = spyOn(templateEffects['nodesApi'], 'copyNode'); - updateNodeSpy = spyOn(templateEffects['nodesApi'], 'updateNode'); + copyNodeSpy = spyOn(templateEffects.nodesApi, 'copyNode'); + updateNodeSpy = spyOn(templateEffects.nodesApi, 'updateNode'); }); afterEach(() => { @@ -162,7 +169,7 @@ describe('TemplateEffects', () => { tick(); expect(store.dispatch['calls'].mostRecent().args[0]).not.toEqual(new CreateFromTemplateSuccess(node)); - expect(store.dispatch['calls'].argsFor(1)[0]).toEqual(new SnackbarErrorAction('APP.MESSAGES.ERRORS.GENERIC')); + expect(showErrorSpy).toHaveBeenCalledWith('APP.MESSAGES.ERRORS.GENERIC'); })); it('should raise name conflict error when copyNode api returns 409', fakeAsync(() => { @@ -172,7 +179,7 @@ describe('TemplateEffects', () => { tick(); expect(store.dispatch['calls'].mostRecent().args[0]).not.toEqual(new CreateFromTemplateSuccess(node)); - expect(store.dispatch['calls'].argsFor(1)[0]).toEqual(new SnackbarErrorAction('APP.MESSAGES.ERRORS.CONFLICT')); + expect(showErrorSpy).toHaveBeenCalledWith('APP.MESSAGES.ERRORS.CONFLICT'); })); it('should resolve error with current node value when updateNode api fails', fakeAsync(() => { diff --git a/projects/aca-content/src/lib/store/effects/template.effects.ts b/projects/aca-content/src/lib/store/effects/template.effects.ts index 4a0643421..06e6dde49 100644 --- a/projects/aca-content/src/lib/store/effects/template.effects.ts +++ b/projects/aca-content/src/lib/store/effects/template.effects.ts @@ -23,7 +23,7 @@ */ import { Actions, ofType, createEffect } from '@ngrx/effects'; -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { map, switchMap, debounceTime, take, catchError } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { @@ -33,11 +33,10 @@ import { CreateFromTemplateSuccess, TemplateActionTypes, getCurrentFolder, - AppStore, - SnackbarErrorAction + AppStore } from '@alfresco/aca-shared/store'; import { NodeTemplateService, TemplateDialogConfig } from '../../services/node-template.service'; -import { AlfrescoApiService } from '@alfresco/adf-core'; +import { AlfrescoApiService, NotificationService } from '@alfresco/adf-core'; import { AppHookService } from '@alfresco/aca-shared'; import { from, Observable, of } from 'rxjs'; import { NodeEntry, NodeBodyUpdate, Node, NodesApi } from '@alfresco/js-api'; @@ -45,6 +44,8 @@ import { MatDialog } from '@angular/material/dialog'; @Injectable() export class TemplateEffects { + private notificationService = inject(NotificationService); + private _nodesApi: NodesApi; get nodesApi(): NodesApi { this._nodesApi = this._nodesApi ?? new NodesApi(this.apiService.getInstance()); @@ -161,9 +162,9 @@ export class TemplateEffects { } if (statusCode !== 409) { - this.store.dispatch(new SnackbarErrorAction('APP.MESSAGES.ERRORS.GENERIC')); + this.notificationService.showError('APP.MESSAGES.ERRORS.GENERIC'); } else { - this.store.dispatch(new SnackbarErrorAction('APP.MESSAGES.ERRORS.CONFLICT')); + this.notificationService.showError('APP.MESSAGES.ERRORS.CONFLICT'); } return of(null); diff --git a/projects/aca-content/src/lib/store/effects/upload.effects.ts b/projects/aca-content/src/lib/store/effects/upload.effects.ts index bbbcfc195..d0300c865 100644 --- a/projects/aca-content/src/lib/store/effects/upload.effects.ts +++ b/projects/aca-content/src/lib/store/effects/upload.effects.ts @@ -24,7 +24,6 @@ import { AppStore, - SnackbarErrorAction, UnlockWriteAction, UploadActionTypes, UploadFilesAction, @@ -32,8 +31,8 @@ import { UploadFolderAction, getCurrentFolder } from '@alfresco/aca-shared/store'; -import { FileUtils } from '@alfresco/adf-core'; -import { Injectable, NgZone, RendererFactory2 } from '@angular/core'; +import { FileUtils, NotificationService } from '@alfresco/adf-core'; +import { inject, Injectable, NgZone, RendererFactory2 } from '@angular/core'; import { Actions, ofType, createEffect } from '@ngrx/effects'; import { Store } from '@ngrx/store'; import { of } from 'rxjs'; @@ -44,6 +43,8 @@ import { UploadService, FileModel } from '@alfresco/adf-content-services'; @Injectable() export class UploadEffects { + private notificationService = inject(NotificationService); + private readonly fileInput: HTMLInputElement; private readonly folderInput: HTMLInputElement; private readonly fileVersionInput: HTMLInputElement; @@ -133,7 +134,7 @@ export class UploadEffects { .getNodeInfo() .pipe( catchError(() => { - this.store.dispatch(new SnackbarErrorAction('VERSION.ERROR.GENERIC')); + this.notificationService.showError('VERSION.ERROR.GENERIC'); return of(null); }) ) diff --git a/projects/aca-shared/store/src/effects/router.effects.ts b/projects/aca-shared/store/src/effects/router.effects.ts index 624ec5829..29a01e00d 100644 --- a/projects/aca-shared/store/src/effects/router.effects.ts +++ b/projects/aca-shared/store/src/effects/router.effects.ts @@ -22,21 +22,21 @@ * from Hyland Software. If not, see . */ -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { Router } from '@angular/router'; import { Actions, ofType, createEffect } from '@ngrx/effects'; import { Node, PathInfo } from '@alfresco/js-api'; import { map } from 'rxjs/operators'; -import { Store } from '@ngrx/store'; -import { AppStore } from '../states/app.state'; import { Location } from '@angular/common'; import { NavigateUrlAction, NavigateRouteAction, NavigateToFolder, NavigateToParentFolder, NavigateToPreviousPage } from '../actions/router.actions'; -import { SnackbarErrorAction } from '../actions/snackbar.actions'; import { RouterActionTypes } from '../actions/router-action-types'; +import { NotificationService } from '@alfresco/adf-core'; @Injectable() export class RouterEffects { - constructor(private store: Store, private actions$: Actions, private router: Router, private location: Location) {} + private notificationService = inject(NotificationService); + + constructor(private actions$: Actions, private router: Router, private location: Location) {} navigateUrl$ = createEffect( () => @@ -143,7 +143,7 @@ export class RouterEffects { this.router.navigate(link); }, 10); } else { - this.store.dispatch(new SnackbarErrorAction('APP.MESSAGES.ERRORS.CANNOT_NAVIGATE_LOCATION')); + this.notificationService.showError('APP.MESSAGES.ERRORS.CANNOT_NAVIGATE_LOCATION'); } }