archy/neode-ui/src/views/appSession/useAppIdentity.ts
Dorian ca5d2cc42a release(v1.7.38-alpha): onboarding auto-heal + silent returning logins + app-store trim
- auth.rs now infers onboarding-complete from setup_complete + password_hash so
  nodes stop bouncing users through the intro wizard after browser clear / update
  / reboot; the flag self-heals to disk on next check
- frontend: "backend uncertain" no longer defaults to /onboarding/intro —
  useOnboarding returns null + callers poll / retry instead of flashing the wizard
- login sounds (synthwave, welcome voice, pop, whoosh, oomph) gated by
  isFirstInstallPhase(); typing sounds unaffected
- removed FIPS app, Nostr Relay, Nostr VPN, Routstr, Penpot from catalog,
  frontend config, Rust AppMetadata + install dispatch + install_penpot_stack;
  docker/fips-ui + docker/nostr-vpn-ui + apps/penpot dirs and 5 icons deleted;
  15 image versions deleted from tx1138, .168, gitea-local registries (.160
  Gitea was 502 at release time — follow-up)
- AIUI baked into frontend release tarball via demo/aiui/; deploy-to-target
  falls back to demo/aiui/ when the AIUI sibling checkout is missing
- prebuild hook syncs app-catalog/catalog.json → public/catalog.json so the
  two copies can no longer drift (was the source of the "apps still visible"
  bug — public/ had stale data)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 13:02:24 -04:00

79 lines
2.3 KiB
TypeScript

/** Composable for managing app identity selection and NIP-07 identity injection */
import { type Ref } from 'vue'
import { rpcClient } from '@/api/rpc-client'
const IDENTITY_KEY = 'archipelago_app_identity_'
export interface SelectedIdentity {
id: string
name: string
did: string
pubkey: string
nostr_pubkey?: string
nostr_npub?: string
}
function isIdentityAwareApp(id: string): boolean {
return id === 'indeedhub' || id === 'nostrudel'
}
export function useAppIdentity(
appId: Ref<string>,
iframeRef: Ref<HTMLIFrameElement | null>,
showIdentityPicker: Ref<boolean>,
) {
function getStoredIdentity(): SelectedIdentity | null {
try {
const stored = localStorage.getItem(IDENTITY_KEY + appId.value)
return stored ? JSON.parse(stored) as SelectedIdentity : null
} catch { return null }
}
function storeIdentity(identity: SelectedIdentity) {
try { localStorage.setItem(IDENTITY_KEY + appId.value, JSON.stringify(identity)) } catch {}
}
async function sendIdentity(identity: SelectedIdentity) {
try {
const challenge = `archipelago-identity:${Date.now()}`
const sigRes = await rpcClient.call<{ signature: string }>({ method: 'identity.sign', params: { id: identity.id, message: challenge } })
iframeRef.value?.contentWindow?.postMessage({
type: 'archipelago:identity', did: identity.did, name: identity.name,
pubkey: identity.pubkey, nostr_pubkey: identity.nostr_pubkey || null,
nostr_npub: identity.nostr_npub || null, challenge, signature: sigRes.signature
}, '*')
} catch {}
}
function onIdentitySelected(identity: SelectedIdentity) {
showIdentityPicker.value = false
storeIdentity(identity)
sendIdentity(identity)
}
/** Called on iframe load to inject identity if the app supports it */
function onIframeLoadIdentity() {
if (isIdentityAwareApp(appId.value)) {
const stored = getStoredIdentity()
if (stored) sendIdentity(stored)
else showIdentityPicker.value = true
}
}
/** Handle identity request messages from iframe */
function handleIdentityRequest() {
const stored = getStoredIdentity()
if (stored) sendIdentity(stored)
else showIdentityPicker.value = true
}
return {
getStoredIdentity,
sendIdentity,
onIdentitySelected,
onIframeLoadIdentity,
handleIdentityRequest,
}
}