import { ref, computed, onUnmounted } from 'vue' import { Accounts } from 'applesauce-accounts' import { accountManager } from '../lib/accounts' import { TEST_PERSONAS, TASTEMAKER_PERSONAS } from '../data/testPersonas' import type { Subscription } from 'rxjs' type Persona = { name: string; nsec: string; pubkey: string } /** * Vue composable for Nostr account management. * Wraps applesauce AccountManager observables as Vue reactive refs. * Provides login methods for Extension, test personas, and private keys. */ export function useAccounts() { const activeAccount = ref(null) const allAccounts = ref([]) const isLoggingIn = ref(false) const loginError = ref(null) const subscriptions: Subscription[] = [] // Subscribe to active account changes const activeSub = accountManager.active$.subscribe((account) => { activeAccount.value = account ?? null }) subscriptions.push(activeSub) // Subscribe to all accounts changes const accountsSub = accountManager.accounts$.subscribe((accounts) => { allAccounts.value = [...accounts] }) subscriptions.push(accountsSub) const isLoggedIn = computed(() => activeAccount.value !== null) const activePubkey = computed(() => activeAccount.value?.pubkey ?? null) const activeName = computed(() => { if (!activeAccount.value) return null // Check test personas for name const allPersonas: Persona[] = [ ...(TEST_PERSONAS as unknown as Persona[]), ...(TASTEMAKER_PERSONAS as unknown as Persona[]), ] const match = allPersonas.find( (p) => p.pubkey === activeAccount.value?.pubkey, ) return match?.name ?? activeAccount.value?.pubkey?.slice(0, 8) ?? 'Unknown' }) /** * Login with a browser extension (NIP-07) */ async function loginWithExtension() { isLoggingIn.value = true loginError.value = null try { const account = await Accounts.ExtensionAccount.fromExtension() accountManager.addAccount(account) accountManager.setActive(account) } catch (err: any) { loginError.value = err.message || 'Extension login failed' console.error('Extension login error:', err) } finally { isLoggingIn.value = false } } /** * Login with a test persona (for development) */ async function loginWithPersona(persona: Persona) { isLoggingIn.value = true loginError.value = null try { const account = Accounts.PrivateKeyAccount.fromKey(persona.nsec) accountManager.addAccount(account) accountManager.setActive(account) } catch (err: any) { loginError.value = err.message || 'Persona login failed' console.error('Persona login error:', err) } finally { isLoggingIn.value = false } } /** * Login with a private key (nsec) */ async function loginWithPrivateKey(nsec: string) { isLoggingIn.value = true loginError.value = null try { const account = Accounts.PrivateKeyAccount.fromKey(nsec) accountManager.addAccount(account) accountManager.setActive(account) } catch (err: any) { loginError.value = err.message || 'Private key login failed' console.error('Private key login error:', err) } finally { isLoggingIn.value = false } } /** * Logout current account */ function logout() { const current = accountManager.active if (current) { accountManager.removeAccount(current) } accountManager.setActive(null as any) } /** * Get all available test personas */ const testPersonas = computed(() => [ ...(TEST_PERSONAS as unknown as Persona[]), ]) const tastemakerPersonas = computed(() => [ ...(TASTEMAKER_PERSONAS as unknown as Persona[]), ]) // Cleanup on unmount onUnmounted(() => { subscriptions.forEach((sub) => sub.unsubscribe()) }) return { // State activeAccount, allAccounts, isLoggedIn, isLoggingIn, loginError, activePubkey, activeName, // Login methods loginWithExtension, loginWithPersona, loginWithPrivateKey, logout, // Personas for dev UI testPersonas, tastemakerPersonas, } }