diff --git a/package-lock.json b/package-lock.json index dbc17f2d6..c56f6b6d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1710,6 +1710,15 @@ "tslib": "^1.9.0" } }, + "@phenomnomnominal/tsquery": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-2.1.0.tgz", + "integrity": "sha512-GfVvugSE0ZJTrscg6UqVpi0Kg+C/w+Alm5wGI+mzjsBLvf3ggiBSNqOzEXwb1MHpwbdfOQ8F2wgRWjCQ/8Jfzw==", + "dev": true, + "requires": { + "esquery": "^1.0.1" + } + }, "@schematics/angular": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.8.3.tgz", @@ -5603,6 +5612,23 @@ "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", "dev": true }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + }, + "dependencies": { + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + } + } + }, "esrecurse": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", @@ -12517,6 +12543,45 @@ "integrity": "sha512-i8sN1i6hld1n0gK8pJEtJIERwz5MgpRvVx3US9tPG1ynJSonFq/VlRx2DFSx0zTyhE4Vl96/rIYSZ6lJdP810g==", "dev": true }, + "rxjs-tslint-rules": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/rxjs-tslint-rules/-/rxjs-tslint-rules-4.8.0.tgz", + "integrity": "sha512-ouk50f5Epj/l2E/5p2extPjFyQ7r+Zoax4inENsMqAi7kdvYjiET4qMCdKq1dTiyCWOJruLhovlJ8oc8NfnV8w==", + "dev": true, + "requires": { + "@phenomnomnominal/tsquery": "^2.0.0", + "decamelize": "^2.0.0", + "resolve": "^1.4.0", + "tslib": "^1.8.0", + "tsutils": "^3.0.0" + }, + "dependencies": { + "decamelize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", + "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", + "dev": true, + "requires": { + "xregexp": "4.0.0" + } + }, + "tsutils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.0.0.tgz", + "integrity": "sha512-LjHBWR0vWAUHWdIAoTjoqi56Kz+FDKBgVEuL+gVPG/Pv7QW5IdaDDeK9Txlr6U0Cmckp5EgCIq1T25qe3J6hyw==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "xregexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", + "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", + "dev": true + } + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", diff --git a/package.json b/package.json index 9bf0b0bf1..cbd7b57d5 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ "prettier": "^1.14.2", "protractor": "^5.4.0", "rimraf": "2.6.2", + "rxjs-tslint-rules": "^4.8.0", "selenium-webdriver": "4.0.0-alpha.1", "ts-node": "~4.1.0", "tsickle": ">=0.29.0", diff --git a/src/app/components/search/search-input-control/search-input-control.component.ts b/src/app/components/search/search-input-control/search-input-control.component.ts index 145a74ec6..cb49534c4 100644 --- a/src/app/components/search/search-input-control/search-input-control.component.ts +++ b/src/app/components/search/search-input-control/search-input-control.component.ts @@ -49,7 +49,7 @@ import { import { MinimalNodeEntity, QueryBody } from 'alfresco-js-api'; import { Subject } from 'rxjs'; import { MatListItem } from '@angular/material'; -import { debounceTime, filter } from 'rxjs/operators'; +import { debounceTime, filter, takeUntil } from 'rxjs/operators'; import { EmptySearchResultComponent, SearchComponent @@ -84,6 +84,8 @@ import { host: { class: 'adf-search-control' } }) export class SearchInputControlComponent implements OnInit, OnDestroy { + onDestroy$: Subject = new Subject(); + /** Toggles whether to use an expanding search control. If false * then a regular input is used. */ @@ -156,7 +158,10 @@ export class SearchInputControlComponent implements OnInit, OnDestroy { constructor(private thumbnailService: ThumbnailService) { this.toggleSearch .asObservable() - .pipe(debounceTime(this.toggleDebounceTime)) + .pipe( + debounceTime(this.toggleDebounceTime), + takeUntil(this.onDestroy$) + ) .subscribe(() => { if (this.expandable && !this.skipToggle) { this.subscriptAnimationState = @@ -194,15 +199,8 @@ export class SearchInputControlComponent implements OnInit, OnDestroy { } ngOnDestroy(): void { - if (this.focusSubject) { - this.focusSubject.unsubscribe(); - this.focusSubject = null; - } - - if (this.toggleSearch) { - this.toggleSearch.unsubscribe(); - this.toggleSearch = null; - } + this.onDestroy$.next(true); + this.onDestroy$.complete(); } searchSubmit(event: any) { @@ -299,7 +297,8 @@ export class SearchInputControlComponent implements OnInit, OnDestroy { this.isSearchBarActive() && ($event.type === 'blur' || $event.type === 'focusout') ); - }) + }), + takeUntil(this.onDestroy$) ) .subscribe(() => { this.toggleSearchBar(); diff --git a/src/app/components/sidenav/sidenav.component.ts b/src/app/components/sidenav/sidenav.component.ts index 5cc26bfb2..20feabc47 100644 --- a/src/app/components/sidenav/sidenav.component.ts +++ b/src/app/components/sidenav/sidenav.component.ts @@ -62,9 +62,9 @@ export class SidenavComponent implements OnInit, OnDestroy { this.store .select(ruleContext) .pipe( - takeUntil(this.onDestroy$), map(rules => rules.repository), - distinctUntilChanged() + distinctUntilChanged(), + takeUntil(this.onDestroy$) ) .subscribe(() => { this.groups = this.extensions.getApplicationNavigation( diff --git a/src/app/store/effects/library.effects.ts b/src/app/store/effects/library.effects.ts index e209e7f05..c5b39b2f6 100644 --- a/src/app/store/effects/library.effects.ts +++ b/src/app/store/effects/library.effects.ts @@ -25,7 +25,7 @@ import { Effect, Actions, ofType } from '@ngrx/effects'; import { Injectable } from '@angular/core'; -import { map, take, switchMap } from 'rxjs/operators'; +import { map, take, mergeMap } from 'rxjs/operators'; import { DeleteLibraryAction, DELETE_LIBRARY, @@ -73,7 +73,7 @@ export class LibraryEffects { @Effect() createLibrary$ = this.actions$.pipe( ofType(CREATE_LIBRARY), - switchMap(() => this.content.createLibrary()), + mergeMap(() => this.content.createLibrary()), map(node => new NavigateLibraryAction(node.entry.guid)) ); diff --git a/tslint.json b/tslint.json index 1ea2872e2..33cde3b23 100644 --- a/tslint.json +++ b/tslint.json @@ -1,5 +1,6 @@ { "rulesDirectory": ["node_modules/codelyzer"], + "extends": ["rxjs-tslint-rules"], "rules": { "arrow-return-shorthand": true, "callable-types": true, @@ -63,6 +64,51 @@ "use-life-cycle-interface": true, "use-pipe-transform-interface": true, "component-class-suffix": true, - "directive-class-suffix": true + "directive-class-suffix": true, + + "rxjs-ban-operators": { + "severity": "error" + }, + "rxjs-no-unsafe-takeuntil": { + "severity": "error" + }, + "rxjs-no-create": { + "severity": "error" + }, + "rxjs-no-ignored-subscribe": { + "severity": "error" + }, + "rxjs-no-subject-unsubscribe": { + "severity": "error" + }, + "rxjs-no-subject-value": { + "severity": "error" + }, + "rxjs-no-unsafe-catch": { + "options": [ + { + "observable": "action(s|\\$)?" + } + ], + "severity": "error" + }, + "rxjs-no-unsafe-switchmap": { + "options": [ + { + "disallow": [ + "add", + "create", + "delete", + "post", + "put", + "remove", + "set", + "update" + ], + "observable": "action(s|\\$)?" + } + ], + "severity": "error" + } } }