import { BehaviorSubject } from "rxjs"; import { GlobalState } from "@bitwarden/common/models/domain/global-state"; import { StorageOptions } from "@bitwarden/common/models/domain/storage-options"; import { StateService as BaseStateService } from "@bitwarden/common/services/state.service"; import { browserSession, sessionSync } from "../decorators/session-sync-observable"; import { Account } from "../models/account"; import { BrowserComponentState } from "../models/browserComponentState"; import { BrowserGroupingsComponentState } from "../models/browserGroupingsComponentState"; import { BrowserSendComponentState } from "../models/browserSendComponentState"; import { BrowserStateService as StateServiceAbstraction } from "./abstractions/browser-state.service"; @browserSession export class BrowserStateService extends BaseStateService implements StateServiceAbstraction { @sessionSync({ initializer: Account.fromJSON as any, // TODO: Remove this any when all any types are removed from Account initializeAs: "record", }) protected accountsSubject: BehaviorSubject<{ [userId: string]: Account }>; @sessionSync({ ctor: String }) protected activeAccountSubject: BehaviorSubject; @sessionSync({ ctor: Boolean }) protected activeAccountUnlockedSubject: BehaviorSubject; @sessionSync({ initializer: Account.fromJSON as any, // TODO: Remove this any when all any types are removed from Account initializeAs: "record", }) protected accountDiskCache: BehaviorSubject>; protected accountDeserializer = Account.fromJSON; async addAccount(account: Account) { // Apply browser overrides to default account values account = new Account(account); await super.addAccount(account); } async getIsAuthenticated(options?: StorageOptions): Promise { // Firefox Private Mode can clash with non-Private Mode because they both read from the same onDiskOptions // Check that there is an account in memory before considering the user authenticated return ( (await super.getIsAuthenticated(options)) && (await this.getAccount(await this.defaultInMemoryOptions())) != null ); } async getBrowserGroupingComponentState( options?: StorageOptions ): Promise { return ( await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions())) )?.groupings; } async setBrowserGroupingComponentState( value: BrowserGroupingsComponentState, options?: StorageOptions ): Promise { const account = await this.getAccount( this.reconcileOptions(options, await this.defaultInMemoryOptions()) ); account.groupings = value; await this.saveAccount( account, this.reconcileOptions(options, await this.defaultInMemoryOptions()) ); } async getBrowserVaultItemsComponentState( options?: StorageOptions ): Promise { return ( await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions())) )?.ciphers; } async setBrowserVaultItemsComponentState( value: BrowserComponentState, options?: StorageOptions ): Promise { const account = await this.getAccount( this.reconcileOptions(options, await this.defaultInMemoryOptions()) ); account.ciphers = value; await this.saveAccount( account, this.reconcileOptions(options, await this.defaultInMemoryOptions()) ); } async getBrowserSendComponentState(options?: StorageOptions): Promise { return ( await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions())) )?.send; } async setBrowserSendComponentState( value: BrowserSendComponentState, options?: StorageOptions ): Promise { const account = await this.getAccount( this.reconcileOptions(options, await this.defaultInMemoryOptions()) ); account.send = value; await this.saveAccount( account, this.reconcileOptions(options, await this.defaultInMemoryOptions()) ); } async getBrowserSendTypeComponentState(options?: StorageOptions): Promise { return ( await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions())) )?.sendType; } async setBrowserSendTypeComponentState( value: BrowserComponentState, options?: StorageOptions ): Promise { const account = await this.getAccount( this.reconcileOptions(options, await this.defaultInMemoryOptions()) ); account.sendType = value; await this.saveAccount( account, this.reconcileOptions(options, await this.defaultInMemoryOptions()) ); } }