feat(settings): show DID on every node + add seed-derived node npub (#13)
- DID: the Identity card read the DID only from localStorage('neode_did'), so
nodes/browsers that never cached it (e.g. .116/.228) showed no DID. Fall back
to the node.did RPC and cache it — the DID now shows everywhere.
- npub: add the node's seed-derived Nostr public key (npub) to the Identity card
next to the DID + onion, fetched from node.nostr-pubkey, with a copy button.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
aa9e0f02b7
commit
9a518db7b8
@ -50,13 +50,19 @@ useBodyScrollLock(showReleaseNotes)
|
||||
const serverTorAddressFromStore = computed(() => store.serverInfo?.['tor-address'] || null)
|
||||
const torAddressFromRpc = ref<string | null>(null)
|
||||
const serverTorAddress = computed(() => serverTorAddressFromStore.value || torAddressFromRpc.value)
|
||||
// Fallback DID fetched from the backend when localStorage doesn't have one
|
||||
// (e.g. a browser/node where onboarding never stored `neode_did`).
|
||||
const didFromRpc = ref<string | null>(null)
|
||||
const userDid = computed(() => {
|
||||
try {
|
||||
return localStorage.getItem('neode_did') || null
|
||||
return localStorage.getItem('neode_did') || didFromRpc.value
|
||||
} catch {
|
||||
return null
|
||||
return didFromRpc.value
|
||||
}
|
||||
})
|
||||
// The node's seed-derived Nostr public key (npub), fetched from the backend.
|
||||
const userNpub = ref<string | null>(null)
|
||||
const copiedNpub = ref(false)
|
||||
|
||||
const copiedOnion = ref(false)
|
||||
const copiedDid = ref(false)
|
||||
@ -100,6 +106,17 @@ async function copyDid() {
|
||||
setTimeout(() => { copiedDid.value = false }, 2000)
|
||||
}
|
||||
|
||||
async function copyNpub() {
|
||||
if (!userNpub.value) return
|
||||
try {
|
||||
await navigator.clipboard.writeText(userNpub.value)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
copiedNpub.value = true
|
||||
setTimeout(() => { copiedNpub.value = false }, 2000)
|
||||
}
|
||||
|
||||
// Load Tor address on mount if not in store
|
||||
async function init() {
|
||||
if (!serverTorAddressFromStore.value) {
|
||||
@ -110,6 +127,29 @@ async function init() {
|
||||
if (import.meta.env.DEV) console.warn('Tor address may not be available yet', e)
|
||||
}
|
||||
}
|
||||
// DID: fall back to the node.did RPC when localStorage doesn't have one, so
|
||||
// the Identity card shows the DID on every node (not just ones where the
|
||||
// browser cached it during onboarding).
|
||||
let storedDid: string | null = null
|
||||
try { storedDid = localStorage.getItem('neode_did') } catch { /* unavailable */ }
|
||||
if (!storedDid) {
|
||||
try {
|
||||
const res = await rpcClient.call<{ did?: string }>({ method: 'node.did' })
|
||||
if (res?.did) {
|
||||
didFromRpc.value = res.did
|
||||
try { localStorage.setItem('neode_did', res.did) } catch { /* unavailable */ }
|
||||
}
|
||||
} catch (e) {
|
||||
if (import.meta.env.DEV) console.warn('node.did unavailable', e)
|
||||
}
|
||||
}
|
||||
// The node's seed-derived Nostr public key (npub) for the Identity card.
|
||||
try {
|
||||
const res = await rpcClient.call<{ nostr_npub?: string }>({ method: 'node.nostr-pubkey' })
|
||||
if (res?.nostr_npub) userNpub.value = res.nostr_npub
|
||||
} catch (e) {
|
||||
if (import.meta.env.DEV) console.warn('node.nostr-pubkey unavailable', e)
|
||||
}
|
||||
}
|
||||
init()
|
||||
</script>
|
||||
@ -1516,8 +1556,8 @@ init()
|
||||
<p class="text-base font-medium text-white/90">{{ t('settings.loggedIn') }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Identity Card: DID + Tor Address -->
|
||||
<div v-if="userDid || serverTorAddress" class="bg-black/20 rounded-xl px-5 py-4 border border-white/10 md:col-span-2 space-y-4">
|
||||
<!-- Identity Card: DID + npub + Tor Address -->
|
||||
<div v-if="userDid || userNpub || serverTorAddress" class="bg-black/20 rounded-xl px-5 py-4 border border-white/10 md:col-span-2 space-y-4">
|
||||
<div v-if="userDid">
|
||||
<div class="flex items-center justify-between gap-2 mb-2">
|
||||
<div class="flex items-center gap-3 min-w-0">
|
||||
@ -1540,7 +1580,29 @@ init()
|
||||
<p class="text-sm font-mono text-white/90 break-all" :title="userDid">{{ userDid }}</p>
|
||||
<p class="text-xs text-white/50 mt-1">{{ t('settings.didHelper') }}</p>
|
||||
</div>
|
||||
<div v-if="serverTorAddress" :class="userDid ? 'pt-4 border-t border-white/10' : ''">
|
||||
<div v-if="userNpub" :class="userDid ? 'pt-4 border-t border-white/10' : ''">
|
||||
<div class="flex items-center justify-between gap-2 mb-2">
|
||||
<div class="flex items-center gap-3 min-w-0">
|
||||
<svg class="w-5 h-5 text-purple-400 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" />
|
||||
</svg>
|
||||
<p class="text-xs font-semibold text-white/60 uppercase tracking-wide">Node npub</p>
|
||||
</div>
|
||||
<button
|
||||
@click="copyNpub"
|
||||
class="shrink-0 px-3 py-1.5 rounded-lg glass-button glass-button-sm text-xs font-medium text-white/90 hover:text-white transition-colors flex items-center gap-1.5"
|
||||
>
|
||||
<svg v-if="!copiedNpub" class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||
</svg>
|
||||
<span v-else class="text-green-400 text-xs">{{ t('common.copied') }}</span>
|
||||
<span v-if="!copiedNpub">{{ t('common.copy') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<p class="text-sm font-mono text-white/90 break-all" :title="userNpub">{{ userNpub }}</p>
|
||||
<p class="text-xs text-white/50 mt-1">Your node's Nostr public key, derived from its seed.</p>
|
||||
</div>
|
||||
<div v-if="serverTorAddress" :class="(userDid || userNpub) ? 'pt-4 border-t border-white/10' : ''">
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<svg class="w-5 h-5 text-amber-400 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" />
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user