[ACS-8959] Introduce new takeUntilDestroyed operator (#4237)

This commit is contained in:
dominikiwanekhyland
2024-11-21 10:49:49 +01:00
committed by GitHub
parent dec6c41e5c
commit adda597f15
52 changed files with 876 additions and 916 deletions

View File

@@ -32,20 +32,19 @@ import {
} from '@alfresco/adf-content-services';
import { ShowHeaderMode, UserPreferencesService } from '@alfresco/adf-core';
import { ContentActionRef, DocumentListPresetRef, SelectionState } from '@alfresco/adf-extensions';
import { OnDestroy, OnInit, OnChanges, ViewChild, SimpleChanges, Directive, inject, HostListener } from '@angular/core';
import { DestroyRef, Directive, HostListener, inject, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild } from '@angular/core';
import { Store } from '@ngrx/store';
import { NodeEntry, Node, NodePaging } from '@alfresco/js-api';
import { Observable, Subject, Subscription } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Node, NodeEntry, NodePaging } from '@alfresco/js-api';
import { Observable, Subscription } from 'rxjs';
import { DocumentBasePageService } from './document-base-page.service';
import {
AppStore,
getCurrentFolder,
getAppSelection,
getCurrentFolder,
isInfoDrawerOpened,
SetSelectedNodesAction,
ViewNodeAction,
ViewNodeExtras,
SetSelectedNodesAction
ViewNodeExtras
} from '@alfresco/aca-shared/store';
import { AppExtensionService } from '../../services/app.extension.service';
import { isLibrary, isLocked } from '../../utils/node.utils';
@@ -54,12 +53,11 @@ import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { Router } from '@angular/router';
import { AppSettingsService } from '../../services/app-settings.service';
import { NavigationHistoryService } from '../../services/navigation-history.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
/* eslint-disable @angular-eslint/directive-class-suffix */
@Directive()
export abstract class PageComponent implements OnInit, OnDestroy, OnChanges {
onDestroy$: Subject<void> = new Subject<void>();
@ViewChild(DocumentListComponent)
documentList: DocumentListComponent;
@@ -90,6 +88,9 @@ export abstract class PageComponent implements OnInit, OnDestroy, OnChanges {
protected router = inject(Router);
protected userPreferencesService = inject(UserPreferencesService);
protected searchAiService = inject(SearchAiService);
protected readonly destroyRef = inject(DestroyRef);
private autoDownloadService = inject(AutoDownloadService, { optional: true });
private navigationHistoryService = inject(NavigationHistoryService);
@@ -106,7 +107,7 @@ export abstract class PageComponent implements OnInit, OnDestroy, OnChanges {
ngOnInit() {
this.extensions
.getCreateActions()
.pipe(takeUntil(this.onDestroy$))
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((actions) => {
this.createActions = actions;
});
@@ -115,7 +116,7 @@ export abstract class PageComponent implements OnInit, OnDestroy, OnChanges {
this.store
.select(getAppSelection)
.pipe(takeUntil(this.onDestroy$))
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((selection) => {
this.selection = selection;
this.canUpdateNode = this.selection.count === 1 && this.content.canUpdateNode(selection.first);
@@ -123,41 +124,41 @@ export abstract class PageComponent implements OnInit, OnDestroy, OnChanges {
this.extensions
.getAllowedToolbarActions()
.pipe(takeUntil(this.onDestroy$))
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((actions) => {
this.actions = actions;
});
this.extensions
.getBulkActions()
.pipe(takeUntil(this.onDestroy$))
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((actions) => {
this.bulkActions = actions;
});
this.extensions
.getViewerToolbarActions()
.pipe(takeUntil(this.onDestroy$))
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((actions) => {
this.viewerToolbarActions = actions;
});
this.store
.select(getCurrentFolder)
.pipe(takeUntil(this.onDestroy$))
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((node) => {
this.canUpload = node && this.content.canUploadContent(node);
});
this.breakpointObserver
.observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape])
.pipe(takeUntil(this.onDestroy$))
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((result) => {
this.isSmallScreen = result.matches;
});
this.searchAiService.toggleSearchAiInput$
.pipe(takeUntil(this.onDestroy$))
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((searchAiInputState) => (this._searchAiInputState = searchAiInputState));
this.setKnowledgeRetrievalState();
@@ -173,8 +174,6 @@ export abstract class PageComponent implements OnInit, OnDestroy, OnChanges {
this.subscriptions.forEach((subscription) => subscription.unsubscribe());
this.subscriptions = [];
this.onDestroy$.next();
this.onDestroy$.complete();
this.store.dispatch(new SetSelectedNodesAction([]));
}

View File

@@ -22,15 +22,13 @@
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
*/
import { Component, HostListener, Input, OnChanges, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { NodeEntry, Node, SiteEntry } from '@alfresco/js-api';
import { Component, DestroyRef, HostListener, inject, Input, OnChanges, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { Node, NodeEntry, SiteEntry } from '@alfresco/js-api';
import { ContentActionRef, DynamicTabComponent, SidebarTabRef } from '@alfresco/adf-extensions';
import { Store } from '@ngrx/store';
import { SetInfoDrawerStateAction, ToggleInfoDrawerAction, infoDrawerPreview } from '@alfresco/aca-shared/store';
import { infoDrawerPreview, SetInfoDrawerStateAction, ToggleInfoDrawerAction } from '@alfresco/aca-shared/store';
import { AppExtensionService } from '../../services/app.extension.service';
import { ContentApiService } from '../../services/content-api.service';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { CommonModule } from '@angular/common';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { InfoDrawerModule } from '@alfresco/adf-core';
@@ -38,6 +36,7 @@ import { TranslateModule } from '@ngx-translate/core';
import { A11yModule } from '@angular/cdk/a11y';
import { ToolbarComponent } from '../toolbar/toolbar.component';
import { ContentService, NodesApiService } from '@alfresco/adf-content-services';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({
standalone: true,
@@ -57,7 +56,7 @@ export class InfoDrawerComponent implements OnChanges, OnInit, OnDestroy {
displayNode: Node | SiteEntry;
tabs: Array<SidebarTabRef> = [];
actions: Array<ContentActionRef> = [];
onDestroy$ = new Subject<boolean>();
preventFromClosing = false;
icon: string = null;
@@ -66,6 +65,8 @@ export class InfoDrawerComponent implements OnChanges, OnInit, OnDestroy {
this.close();
}
private readonly destroyRef = inject(DestroyRef);
constructor(
private store: Store<any>,
private contentApi: ContentApiService,
@@ -78,26 +79,24 @@ export class InfoDrawerComponent implements OnChanges, OnInit, OnDestroy {
this.tabs = this.extensions.getSidebarTabs();
this.extensions
.getAllowedSidebarActions()
.pipe(takeUntil(this.onDestroy$))
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((actions) => {
this.actions = actions;
});
this.store
.select(infoDrawerPreview)
.pipe(takeUntil(this.onDestroy$))
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((isInfoDrawerPreviewOpened) => {
this.preventFromClosing = isInfoDrawerPreviewOpened;
});
this.nodesService.nodeUpdated.pipe(takeUntil(this.onDestroy$)).subscribe((node: any) => {
this.nodesService.nodeUpdated.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((node: any) => {
this.node.entry = node;
});
}
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
if (!this.preventFromClosing) {
this.store.dispatch(new SetInfoDrawerStateAction(false));
}

View File

@@ -22,14 +22,14 @@
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
*/
import { Component, ViewEncapsulation, Input, OnDestroy } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Component, Input, ViewEncapsulation } from '@angular/core';
import { Observable } from 'rxjs';
import { AppService } from '../../services/app.service';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({
standalone: true,
@@ -40,23 +40,17 @@ import { MatIconModule } from '@angular/material/icon';
encapsulation: ViewEncapsulation.None,
host: { class: 'aca-page-layout' }
})
export class PageLayoutComponent implements OnDestroy {
export class PageLayoutComponent {
@Input()
hasError = false;
private onDestroy$ = new Subject<boolean>();
appNavNarMode$: Observable<'collapsed' | 'expanded'>;
constructor(private appService: AppService) {
this.appNavNarMode$ = appService.appNavNarMode$.pipe(takeUntil(this.onDestroy$));
this.appNavNarMode$ = appService.appNavNarMode$.pipe(takeUntilDestroyed());
}
toggleClick() {
this.appService.toggleAppNavBar$.next();
}
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
}

View File

@@ -25,7 +25,9 @@
import { ContextActionsDirective } from './contextmenu.directive';
import { ContextMenu, CustomContextMenu } from '@alfresco/aca-shared/store';
import { ContentActionRef, ContentActionType } from '@alfresco/adf-extensions';
import { fakeAsync, tick } from '@angular/core/testing';
import { fakeAsync, TestBed, tick } from '@angular/core/testing';
import { Store } from '@ngrx/store';
import { Injector, runInInjectionContext } from '@angular/core';
const customActionsMock: ContentActionRef[] = [
{
@@ -46,7 +48,13 @@ describe('ContextActionsDirective', () => {
};
beforeEach(() => {
directive = new ContextActionsDirective(storeMock);
TestBed.configureTestingModule({
imports: [ContextActionsDirective],
providers: [{ provide: Store, useValue: storeMock }]
});
runInInjectionContext(TestBed.inject(Injector), () => {
directive = new ContextActionsDirective(storeMock);
});
});
it('should not render context menu when `enabled` property is false', () => {

View File

@@ -22,22 +22,20 @@
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
*/
import { Directive, HostListener, Input, OnInit, OnDestroy } from '@angular/core';
import { debounceTime, takeUntil } from 'rxjs/operators';
import { DestroyRef, Directive, HostListener, inject, Input, OnInit } from '@angular/core';
import { debounceTime } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { Store } from '@ngrx/store';
import { AppStore, ContextMenu, CustomContextMenu } from '@alfresco/aca-shared/store';
import { ContentActionRef } from '@alfresco/adf-extensions';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Directive({
standalone: true,
selector: '[acaContextActions]',
exportAs: 'acaContextActions'
})
export class ContextActionsDirective implements OnInit, OnDestroy {
private execute$: Subject<any> = new Subject();
onDestroy$: Subject<boolean> = new Subject<boolean>();
export class ContextActionsDirective implements OnInit {
// eslint-disable-next-line
@Input('acaContextEnable')
enabled = true;
@@ -59,10 +57,14 @@ export class ContextActionsDirective implements OnInit, OnDestroy {
}
}
private execute$: Subject<any> = new Subject();
private readonly destroyRef = inject(DestroyRef);
constructor(private store: Store<AppStore>) {}
ngOnInit() {
this.execute$.pipe(debounceTime(300), takeUntil(this.onDestroy$)).subscribe((event: MouseEvent) => {
this.execute$.pipe(debounceTime(300), takeUntilDestroyed(this.destroyRef)).subscribe((event: MouseEvent) => {
if (this.customActions?.length) {
this.store.dispatch(new CustomContextMenu(event, this.customActions));
} else {
@@ -70,12 +72,6 @@ export class ContextActionsDirective implements OnInit, OnDestroy {
}
});
}
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
execute(event: MouseEvent, target: Element) {
if (!this.isSelected(target)) {
target.dispatchEvent(new MouseEvent('click'));

View File

@@ -23,10 +23,11 @@
*/
import { PaginationDirective } from './pagination.directive';
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { UserPreferencesService, AppConfigService, PaginationComponent, PaginationModel } from '@alfresco/adf-core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AppConfigService, PaginationComponent, PaginationModel, UserPreferencesService } from '@alfresco/adf-core';
import { initialState, LibTestingModule } from '../testing/lib-testing-module';
import { provideMockStore } from '@ngrx/store/testing';
import { Injector, runInInjectionContext } from '@angular/core';
describe('PaginationDirective', () => {
let preferences: UserPreferencesService;
@@ -45,12 +46,13 @@ describe('PaginationDirective', () => {
config = TestBed.inject(AppConfigService);
fixture = TestBed.createComponent(PaginationComponent);
pagination = fixture.componentInstance;
directive = new PaginationDirective(pagination, preferences, config);
runInInjectionContext(TestBed.inject(Injector), () => {
directive = new PaginationDirective(pagination, preferences, config);
});
});
afterEach(() => {
fixture.destroy();
directive.ngOnDestroy();
});
it('should setup supported page sizes from app config', () => {

View File

@@ -22,31 +22,23 @@
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
*/
import { Directive, OnInit, OnDestroy } from '@angular/core';
import { PaginationComponent, UserPreferencesService, PaginationModel, AppConfigService } from '@alfresco/adf-core';
import { Subscription } from 'rxjs';
import { DestroyRef, Directive, inject, OnInit } from '@angular/core';
import { AppConfigService, PaginationComponent, PaginationModel, UserPreferencesService } from '@alfresco/adf-core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Directive({
standalone: true,
selector: '[acaPagination]'
})
export class PaginationDirective implements OnInit, OnDestroy {
private subscriptions: Subscription[] = [];
export class PaginationDirective implements OnInit {
private readonly destroyRef = inject(DestroyRef);
constructor(private pagination: PaginationComponent, private preferences: UserPreferencesService, private config: AppConfigService) {}
ngOnInit() {
this.pagination.supportedPageSizes = this.config.get('pagination.supportedPageSizes');
this.subscriptions.push(
this.pagination.changePageSize.subscribe((event: PaginationModel) => {
this.preferences.paginationSize = event.maxItems;
})
);
}
ngOnDestroy() {
this.subscriptions.forEach((subscription) => subscription.unsubscribe());
this.subscriptions = [];
this.pagination.changePageSize.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event: PaginationModel) => {
this.preferences.paginationSize = event.maxItems;
});
}
}

View File

@@ -22,20 +22,20 @@
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
*/
import { inject, Injectable, OnDestroy } from '@angular/core';
import { AuthenticationService, AppConfigService, PageTitleService, UserPreferencesService, NotificationService } from '@alfresco/adf-core';
import { Observable, BehaviorSubject, Subject } from 'rxjs';
import { inject, Injectable } from '@angular/core';
import { AppConfigService, AuthenticationService, NotificationService, PageTitleService, UserPreferencesService } from '@alfresco/adf-core';
import { BehaviorSubject, Observable, Subject } from 'rxjs';
import {
AlfrescoApiService,
FileUploadErrorEvent,
SearchQueryBuilderService,
SharedLinksApiService,
UploadService,
FileUploadErrorEvent
UploadService
} from '@alfresco/adf-content-services';
import { OverlayContainer } from '@angular/cdk/overlay';
import { ActivatedRoute, ActivationEnd, NavigationStart, Router } from '@angular/router';
import { filter, map } from 'rxjs/operators';
import { AppStore, SetCurrentUrlAction, SetRepositoryInfoAction, SetUserProfileAction, ResetSelectionAction } from '@alfresco/aca-shared/store';
import { AppStore, ResetSelectionAction, SetCurrentUrlAction, SetRepositoryInfoAction, SetUserProfileAction } from '@alfresco/aca-shared/store';
import { ContentApiService } from './content-api.service';
import { RouterExtensionService } from './router.extension.service';
import { Store } from '@ngrx/store';
@@ -50,7 +50,7 @@ import { MatDialog } from '@angular/material/dialog';
providedIn: 'root'
})
// After moving shell to ADF to core, AppService will implement ShellAppService
export class AppService implements ShellAppService, OnDestroy {
export class AppService implements ShellAppService {
private notificationService = inject(NotificationService);
private matDialog = inject(MatDialog);
private ready: BehaviorSubject<boolean>;
@@ -67,8 +67,6 @@ export class AppService implements ShellAppService, OnDestroy {
hideSidenavConditions = ['/preview/'];
minimizeSidenavConditions = ['/search'];
onDestroy$ = new Subject<boolean>();
/**
* Whether `withCredentials` mode is enabled.
* Usually means that `Kerberos` mode is used.
@@ -121,11 +119,6 @@ export class AppService implements ShellAppService, OnDestroy {
});
}
ngOnDestroy(): void {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
init(): void {
this.alfrescoApiService.getInstance().on('error', (error: { status: number; response: any }) => {
if (error.status === 401 && !this.alfrescoApiService.isExcludedErrorListener(error?.response?.req?.url)) {