import { BehaviorSubject } from "rxjs"; import { ProxySigner } from "./proxy-signer.js"; export class AccountManager { types = new Map(); active$ = new BehaviorSubject(undefined); get active() { return this.active$.value; } accounts$ = new BehaviorSubject([]); get accounts() { return this.accounts$.value; } /** Proxy signer for currently active account */ signer; /** Disable request queueing for any accounts added to this manager */ disableQueue; constructor() { this.signer = new ProxySigner(this.active$, "No active account"); } // Account type CRUD /** Add account type class */ registerType(accountType) { if (!accountType.type) throw new Error(`Account class missing static "type" field`); if (this.types.has(accountType.type)) throw new Error(`An account type of ${accountType.type} already exists`); this.types.set(accountType.type, accountType); } /** Remove account type */ unregisterType(type) { this.types.delete(type); } // Accounts CRUD /** gets an account in the manager */ getAccount(id) { if (typeof id === "string") return this.accounts$.value.find((a) => a.id === id); else if (this.accounts$.value.includes(id)) return id; else return undefined; } /** Return the first account for a pubkey */ getAccountForPubkey(pubkey) { return Object.values(this.accounts$.value).find((account) => account.pubkey === pubkey); } /** Returns all accounts for a pubkey */ getAccountsForPubkey(pubkey) { return Object.values(this.accounts$.value).filter((account) => account.pubkey === pubkey); } /** adds an account to the manager */ addAccount(account) { if (this.getAccount(account.id)) return; // copy the disableQueue flag only if its set if (this.disableQueue !== undefined && account.disableQueue !== undefined) { account.disableQueue = this.disableQueue; } this.accounts$.next([...this.accounts$.value, account]); } /** Removes an account from the manager */ removeAccount(account) { const id = typeof account === "string" ? account : account.id; this.accounts$.next(this.accounts$.value.filter((a) => a.id !== id)); // if the removed account was active, clear the active account if (this.active$.value?.id === id) this.active$.next(undefined); } /** Replaces an account with another */ replaceAccount(old, account) { this.addAccount(account); // if the old account was active, switch to the new one const id = typeof account === "string" ? account : account.id; if (this.active$.value?.id === id) this.setActive(account); this.removeAccount(old); } // Active account methods /** Returns the currently active account */ getActive() { return this.active$.value; } /** Sets the currently active account */ setActive(id) { const account = this.getAccount(id); if (!account) throw new Error("Cant find account with that ID"); if (this.active$.value?.id !== account.id) { this.active$.next(account); } } /** Clears the currently active account */ clearActive() { this.active$.next(undefined); } // Metadata CRUD /** sets the metadata on an account */ setAccountMetadata(id, metadata) { const account = this.getAccount(id); if (!account) throw new Error("Cant find account with that ID"); account.metadata = metadata; } /** sets the metadata on an account */ getAccountMetadata(id) { const account = this.getAccount(id); if (!account) throw new Error("Cant find account with that ID"); return account.metadata; } /** Removes all metadata on the account */ clearAccountMetadata(id) { const account = this.getAccount(id); if (!account) throw new Error("Cant find account with that ID"); account.metadata = undefined; } // Serialize / Deserialize /** Returns an array of serialized accounts */ toJSON(quite = false) { const accounts = []; for (const account of this.accounts) { try { accounts.push(account.toJSON()); } catch (error) { if (!quite) throw error; } } return accounts; } /** * Restores all accounts from an array of serialized accounts * NOTE: this will clear all existing accounts */ fromJSON(accounts, quite = false) { for (const json of accounts) { try { const AccountType = this.types.get(json.type); if (!AccountType) { if (!quite) throw new Error(`Missing account type ${json.type}`); else continue; } const account = AccountType.fromJSON(json); this.addAccount(account); } catch (error) { if (!quite) throw error; else console.log(`Failed to load account`, error); } } } }