From ef90d37a440acdcced7c9ec525eee283db62e367 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Thu, 5 Oct 2023 08:41:55 -0400 Subject: [PATCH] Replay without refcount for app state values --- libs/common/spec/utils.ts | 10 +++++++ .../src/auth/services/account.service.spec.ts | 29 +++++++++++++++++++ .../src/auth/services/account.service.ts | 4 +-- .../services/default-user-state.provider.ts | 4 ++- 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/libs/common/spec/utils.ts b/libs/common/spec/utils.ts index 87a9d630aea..b41006c7a87 100644 --- a/libs/common/spec/utils.ts +++ b/libs/common/spec/utils.ts @@ -46,6 +46,16 @@ export const mockFromJson = (stub: any) => (stub + "_fromJSON") as any; export function trackEmissions(observable: Observable): T[] { const emissions: T[] = []; observable.subscribe((value) => { + switch (value) { + case undefined: + case null: + emissions.push(value); + return; + default: + // process by type + break; + } + switch (typeof value) { case "string": case "number": diff --git a/libs/common/src/auth/services/account.service.spec.ts b/libs/common/src/auth/services/account.service.spec.ts index ac801e2e850..75cf5badeb6 100644 --- a/libs/common/src/auth/services/account.service.spec.ts +++ b/libs/common/src/auth/services/account.service.spec.ts @@ -30,6 +30,35 @@ describe("accountService", () => { jest.resetAllMocks(); }); + describe("activeAccount$", () => { + it("should emit undefined if no account is active", () => { + const emissions = trackEmissions(sut.activeAccount$); + + expect(emissions).toEqual([undefined]); + }); + + it("should emit the active account and status", async () => { + const emissions = trackEmissions(sut.activeAccount$); + sut.addAccount(userId, userInfo(AuthenticationStatus.Unlocked)); + sut.switchAccount(userId); + + expect(emissions).toEqual([ + undefined, // initial value + { id: userId, ...userInfo(AuthenticationStatus.Unlocked) }, + ]); + }); + + it("should remember the last emitted value", async () => { + sut.addAccount(userId, userInfo(AuthenticationStatus.Unlocked)); + sut.switchAccount(userId); + + expect(await firstValueFrom(sut.activeAccount$)).toEqual({ + id: userId, + ...userInfo(AuthenticationStatus.Unlocked), + }); + }); + }); + describe("addAccount", () => { it("should throw if the account already exists", () => { sut.addAccount(userId, userInfo(AuthenticationStatus.Unlocked)); diff --git a/libs/common/src/auth/services/account.service.ts b/libs/common/src/auth/services/account.service.ts index d8407ab1151..7e619f26f01 100644 --- a/libs/common/src/auth/services/account.service.ts +++ b/libs/common/src/auth/services/account.service.ts @@ -4,7 +4,7 @@ import { combineLatestWith, distinctUntilChanged, map, - share, + shareReplay, } from "rxjs"; import { AccountInfo, InternalAccountService } from "../../auth/abstractions/account.service"; @@ -24,7 +24,7 @@ export class AccountServiceImplementation implements InternalAccountService { combineLatestWith(this.accounts$), map(([id, accounts]) => (id ? { id, ...accounts[id] } : undefined)), distinctUntilChanged(), - share() + shareReplay({ bufferSize: 1, refCount: false }) ); accountLock$ = this.lock.asObservable(); accountLogout$ = this.logout.asObservable(); diff --git a/libs/common/src/platform/services/default-user-state.provider.ts b/libs/common/src/platform/services/default-user-state.provider.ts index 3fa0fef3451..b83d7bcde1e 100644 --- a/libs/common/src/platform/services/default-user-state.provider.ts +++ b/libs/common/src/platform/services/default-user-state.provider.ts @@ -5,6 +5,7 @@ import { firstValueFrom, map, share, + shareReplay, switchMap, tap, } from "rxjs"; @@ -95,7 +96,8 @@ class DefaultUserState implements UserState { account != null && account.id != null ? userKeyBuilder(account.id, this.keyDefinition) : null - ) + ), + shareReplay({ bufferSize: 1, refCount: false }) ); const activeAccountData$ = this.formattedKey$.pipe(