diff --git a/apps/browser/src/popup/app.component.ts b/apps/browser/src/popup/app.component.ts index bc383715ea..7524aec22d 100644 --- a/apps/browser/src/popup/app.component.ts +++ b/apps/browser/src/popup/app.component.ts @@ -56,7 +56,7 @@ export class AppComponent implements OnInit, OnDestroy { // Clear them aggressively to make sure this doesn't occur await this.clearComponentStates(); - this.stateService.activeAccount.pipe(takeUntil(this.destroy$)).subscribe((userId) => { + this.stateService.activeAccount$.pipe(takeUntil(this.destroy$)).subscribe((userId) => { this.activeUserId = userId; }); @@ -84,7 +84,7 @@ export class AppComponent implements OnInit, OnDestroy { }); } - if (this.stateService.activeAccount.getValue() == null) { + if (this.activeUserId === null) { this.router.navigate(["home"]); } }); diff --git a/apps/desktop/src/app/accounts/lock.component.ts b/apps/desktop/src/app/accounts/lock.component.ts index c7224113f9..e7e5c33715 100644 --- a/apps/desktop/src/app/accounts/lock.component.ts +++ b/apps/desktop/src/app/accounts/lock.component.ts @@ -1,4 +1,4 @@ -import { Component, NgZone, OnDestroy } from "@angular/core"; +import { Component, NgZone } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; import { ipcRenderer } from "electron"; @@ -22,7 +22,7 @@ const BroadcasterSubscriptionId = "LockComponent"; selector: "app-lock", templateUrl: "lock.component.html", }) -export class LockComponent extends BaseLockComponent implements OnDestroy { +export class LockComponent extends BaseLockComponent { private deferFocus: boolean = null; authenicatedUrl = "vault"; unAuthenicatedUrl = "update-temp-password"; @@ -103,6 +103,7 @@ export class LockComponent extends BaseLockComponent implements OnDestroy { } ngOnDestroy() { + super.ngOnDestroy(); this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); } diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index dc089e20e4..f2400b31fb 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -1,6 +1,7 @@ import { Component, NgZone, + OnDestroy, OnInit, SecurityContext, Type, @@ -77,7 +78,7 @@ const systemTimeoutOptions = { `, }) -export class AppComponent implements OnInit { +export class AppComponent implements OnInit, OnDestroy { @ViewChild("settings", { read: ViewContainerRef, static: true }) settingsRef: ViewContainerRef; @ViewChild("premium", { read: ViewContainerRef, static: true }) premiumRef: ViewContainerRef; @ViewChild("passwordHistory", { read: ViewContainerRef, static: true }) @@ -129,7 +130,7 @@ export class AppComponent implements OnInit { ) {} ngOnInit() { - this.stateService.activeAccount.pipe(takeUntil(this.destroy$)).subscribe((userId) => { + this.stateService.activeAccount$.pipe(takeUntil(this.destroy$)).subscribe((userId) => { this.activeUserId = userId; }); diff --git a/apps/desktop/src/app/layout/search/search-bar.service.ts b/apps/desktop/src/app/layout/search/search-bar.service.ts index 340ee3a698..24f3d0513f 100644 --- a/apps/desktop/src/app/layout/search/search-bar.service.ts +++ b/apps/desktop/src/app/layout/search/search-bar.service.ts @@ -8,15 +8,16 @@ export type SearchBarState = { @Injectable() export class SearchBarService { - searchText = new BehaviorSubject(null); + private searchTextSubject = new BehaviorSubject(null); + searchText$ = this.searchTextSubject.asObservable(); private _state = { enabled: false, placeholderText: "", }; - // tslint:disable-next-line:member-ordering - state = new BehaviorSubject(this._state); + private stateSubject = new BehaviorSubject(this._state); + state$ = this.stateSubject.asObservable(); setEnabled(enabled: boolean) { this._state.enabled = enabled; @@ -29,10 +30,10 @@ export class SearchBarService { } setSearchText(value: string) { - this.searchText.next(value); + this.searchTextSubject.next(value); } private updateState() { - this.state.next(this._state); + this.stateSubject.next(this._state); } } diff --git a/apps/desktop/src/app/layout/search/search.component.ts b/apps/desktop/src/app/layout/search/search.component.ts index e836ebd91a..6f5f7a69fd 100644 --- a/apps/desktop/src/app/layout/search/search.component.ts +++ b/apps/desktop/src/app/layout/search/search.component.ts @@ -1,5 +1,6 @@ import { Component, OnDestroy, OnInit } from "@angular/core"; import { UntypedFormControl } from "@angular/forms"; +import { Subscription } from "rxjs"; import { StateService } from "@bitwarden/common/abstractions/state.service"; @@ -13,8 +14,10 @@ export class SearchComponent implements OnInit, OnDestroy { state: SearchBarState; searchText: UntypedFormControl = new UntypedFormControl(null); + private activeAccountSubscription: Subscription; + constructor(private searchBarService: SearchBarService, private stateService: StateService) { - this.searchBarService.state.subscribe((state) => { + this.searchBarService.state$.subscribe((state) => { this.state = state; }); @@ -24,13 +27,13 @@ export class SearchComponent implements OnInit, OnDestroy { } ngOnInit() { - this.stateService.activeAccount.subscribe((value) => { + this.activeAccountSubscription = this.stateService.activeAccount$.subscribe((value) => { this.searchBarService.setSearchText(""); this.searchText.patchValue(""); }); } ngOnDestroy() { - this.stateService.activeAccount.unsubscribe(); + this.activeAccountSubscription.unsubscribe(); } } diff --git a/apps/desktop/src/app/send/send.component.ts b/apps/desktop/src/app/send/send.component.ts index 5f9b3cde7c..d8b68d842c 100644 --- a/apps/desktop/src/app/send/send.component.ts +++ b/apps/desktop/src/app/send/send.component.ts @@ -56,7 +56,7 @@ export class SendComponent extends BaseSendComponent implements OnInit, OnDestro policyService, logService ); - this.searchBarService.searchText.subscribe((searchText) => { + this.searchBarService.searchText$.subscribe((searchText) => { this.searchText = searchText; this.searchTextChanged(); }); diff --git a/apps/desktop/src/app/vault/ciphers.component.ts b/apps/desktop/src/app/vault/ciphers.component.ts index 7505206f50..30fcbd270b 100644 --- a/apps/desktop/src/app/vault/ciphers.component.ts +++ b/apps/desktop/src/app/vault/ciphers.component.ts @@ -14,7 +14,7 @@ export class CiphersComponent extends BaseCiphersComponent { constructor(searchService: SearchService, searchBarService: SearchBarService) { super(searchService); - searchBarService.searchText.subscribe((searchText) => { + searchBarService.searchText$.subscribe((searchText) => { this.searchText = searchText; this.search(200); }); diff --git a/libs/angular/src/components/lock.component.ts b/libs/angular/src/components/lock.component.ts index b16717ea7f..83d5744dfa 100644 --- a/libs/angular/src/components/lock.component.ts +++ b/libs/angular/src/components/lock.component.ts @@ -1,5 +1,6 @@ -import { Directive, NgZone, OnInit } from "@angular/core"; +import { Directive, NgZone, OnDestroy, OnInit } from "@angular/core"; import { Router } from "@angular/router"; +import { Subscription } from "rxjs"; import { take } from "rxjs/operators"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; @@ -20,7 +21,7 @@ import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCry import { SecretVerificationRequest } from "@bitwarden/common/models/request/secretVerificationRequest"; @Directive() -export class LockComponent implements OnInit { +export class LockComponent implements OnInit, OnDestroy { masterPassword = ""; pin = ""; showPassword = false; @@ -39,6 +40,8 @@ export class LockComponent implements OnInit { private invalidPinAttempts = 0; private pinSet: [boolean, boolean]; + private activeAccountSubscription: Subscription; + constructor( protected router: Router, protected i18nService: I18nService, @@ -57,11 +60,15 @@ export class LockComponent implements OnInit { async ngOnInit() { // Load the first and observe updates await this.load(); - this.stateService.activeAccount.subscribe(async () => { + this.activeAccountSubscription = this.stateService.activeAccount$.subscribe(async () => { await this.load(); }); } + ngOnDestroy() { + this.activeAccountSubscription.unsubscribe(); + } + async submit() { if (this.pinLock && (this.pin == null || this.pin === "")) { this.platformUtilsService.showToast( diff --git a/libs/common/spec/services/folder.service.spec.ts b/libs/common/spec/services/folder.service.spec.ts index 697ae491f5..660d4a953c 100644 --- a/libs/common/spec/services/folder.service.spec.ts +++ b/libs/common/spec/services/folder.service.spec.ts @@ -32,7 +32,7 @@ describe("Folder Service", () => { stateService.getEncryptedFolders().resolves({ "1": folderData("1", "test"), }); - stateService.activeAccount.returns(activeAccount); + stateService.activeAccount$.returns(activeAccount); stateService.activeAccountUnlocked.returns(activeAccountUnlocked); (window as any).bitwardenContainerService = new ContainerService(cryptoService); diff --git a/libs/common/src/abstractions/state.service.ts b/libs/common/src/abstractions/state.service.ts index ae31a222cc..b99d8aef86 100644 --- a/libs/common/src/abstractions/state.service.ts +++ b/libs/common/src/abstractions/state.service.ts @@ -26,9 +26,8 @@ import { SendView } from "../models/view/sendView"; export abstract class StateService { accounts: BehaviorSubject<{ [userId: string]: T }>; - activeAccount: BehaviorSubject; - - activeAccountUnlocked: Observable; + activeAccount$: Observable; + activeAccountUnlocked$: Observable; addAccount: (account: T) => Promise; setActiveUser: (userId: string) => Promise; diff --git a/libs/common/src/services/environment.service.ts b/libs/common/src/services/environment.service.ts index 271fb87c45..1d62d471ad 100644 --- a/libs/common/src/services/environment.service.ts +++ b/libs/common/src/services/environment.service.ts @@ -22,7 +22,7 @@ export class EnvironmentService implements EnvironmentServiceAbstraction { private scimUrl: string = null; constructor(private stateService: StateService) { - this.stateService.activeAccount.subscribe(async () => { + this.stateService.activeAccount$.subscribe(async () => { await this.setUrlsFromStorage(); }); } diff --git a/libs/common/src/services/folder/folder.service.ts b/libs/common/src/services/folder/folder.service.ts index 222ea44fb3..4b3530cb81 100644 --- a/libs/common/src/services/folder/folder.service.ts +++ b/libs/common/src/services/folder/folder.service.ts @@ -25,7 +25,7 @@ export class FolderService implements InternalFolderServiceAbstraction { private cipherService: CipherService, private stateService: StateService ) { - this.stateService.activeAccountUnlocked.subscribe(async (unlocked) => { + this.stateService.activeAccountUnlocked$.subscribe(async (unlocked) => { if ((Utils.global as any).bitwardenContainerService == null) { return; } diff --git a/libs/common/src/services/state.service.ts b/libs/common/src/services/state.service.ts index bc54c76ffa..39f3488649 100644 --- a/libs/common/src/services/state.service.ts +++ b/libs/common/src/services/state.service.ts @@ -55,8 +55,11 @@ export class StateService< > implements StateServiceAbstraction { accounts = new BehaviorSubject<{ [userId: string]: TAccount }>({}); - activeAccount = new BehaviorSubject(null); - activeAccountUnlocked = new BehaviorSubject(false); + private activeAccountSubject = new BehaviorSubject(null); + activeAccount$ = this.activeAccountSubject.asObservable(); + + private activeAccountUnlockedSubject = new BehaviorSubject(false); + activeAccountUnlocked$ = this.activeAccountUnlockedSubject.asObservable(); private hasBeenInited = false; private isRecoveredSession = false; @@ -73,17 +76,17 @@ export class StateService< protected useAccountCache: boolean = true ) { // If the account gets changed, verify the new account is unlocked - this.activeAccount.subscribe(async (userId) => { - if (userId == null && this.activeAccountUnlocked.getValue() == false) { + this.activeAccountSubject.subscribe(async (userId) => { + if (userId == null && this.activeAccountUnlockedSubject.getValue() == false) { return; } else if (userId == null) { - this.activeAccountUnlocked.next(false); + this.activeAccountUnlockedSubject.next(false); } // FIXME: This should be refactored into AuthService or a similar service, // as checking for the existance of the crypto key is a low level // implementation detail. - this.activeAccountUnlocked.next((await this.getCryptoMasterKey()) != null); + this.activeAccountUnlockedSubject.next((await this.getCryptoMasterKey()) != null); }); } @@ -125,7 +128,7 @@ export class StateService< state.activeUserId = storedActiveUser; } await this.pushAccounts(); - this.activeAccount.next(state.activeUserId); + this.activeAccountSubject.next(state.activeUserId); return state; }); @@ -154,7 +157,7 @@ export class StateService< await this.scaffoldNewAccountStorage(account); await this.setLastActive(new Date().getTime(), { userId: account.profile.userId }); await this.setActiveUser(account.profile.userId); - this.activeAccount.next(account.profile.userId); + this.activeAccountSubject.next(account.profile.userId); } async setActiveUser(userId: string): Promise { @@ -162,7 +165,7 @@ export class StateService< await this.updateState(async (state) => { state.activeUserId = userId; await this.storageService.save(keys.activeUserId, userId); - this.activeAccount.next(state.activeUserId); + this.activeAccountSubject.next(state.activeUserId); return state; }); @@ -497,12 +500,12 @@ export class StateService< this.reconcileOptions(options, await this.defaultInMemoryOptions()) ); - if (options.userId == this.activeAccount.getValue()) { + if (options.userId == this.activeAccountSubject.getValue()) { const nextValue = value != null; // Avoid emitting if we are already unlocked - if (this.activeAccountUnlocked.getValue() != nextValue) { - this.activeAccountUnlocked.next(nextValue); + if (this.activeAccountUnlockedSubject.getValue() != nextValue) { + this.activeAccountUnlockedSubject.next(nextValue); } } }