2026-03-21 02:43:28 +00:00
|
|
|
<template>
|
|
|
|
|
<div class="pb-6">
|
|
|
|
|
<!-- Quick Actions + HW Banner -->
|
|
|
|
|
<Web5QuickActions
|
|
|
|
|
:showStagger="showStagger"
|
|
|
|
|
:profitsBreakdown="profitsBreakdown"
|
|
|
|
|
:networkingProfitsDisplay="networkingProfitsDisplay"
|
|
|
|
|
:userDid="userDid"
|
|
|
|
|
:didStatus="didStatus"
|
|
|
|
|
:didCopied="didCopied"
|
|
|
|
|
:creatingDid="creatingDid"
|
|
|
|
|
:dhtDid="dhtDid"
|
|
|
|
|
:dhtDidCopied="dhtDidCopied"
|
|
|
|
|
:publishingDht="publishingDht"
|
|
|
|
|
:walletConnected="walletConnected"
|
|
|
|
|
:connectingWallet="connectingWallet"
|
|
|
|
|
:nostrRelayStats="nostrRelaysRef?.nostrRelayStats ?? null"
|
|
|
|
|
:connectedNodesCount="connectedNodesRef?.peers?.length ?? 0"
|
|
|
|
|
:detectedHwWallets="detectedHwWallets"
|
|
|
|
|
@copyDid="copyDid"
|
|
|
|
|
@showDidDocument="showDidDocument"
|
|
|
|
|
@createDid="createDID"
|
|
|
|
|
@copyDhtDid="copyDhtDid"
|
|
|
|
|
@refreshDhtDid="refreshDhtDid"
|
|
|
|
|
@publishDhtDid="publishDhtDid"
|
|
|
|
|
@connectWallet="connectWallet"
|
|
|
|
|
@manageRelays="nostrRelaysRef?.openRelaysModal()"
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<!-- DID Document Modal -->
|
|
|
|
|
<Teleport to="body">
|
|
|
|
|
<div v-if="showDidDocModal" class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-md" @click.self="showDidDocModal = false" @keydown.escape="showDidDocModal = false">
|
|
|
|
|
<div class="glass-card p-6 max-w-2xl w-full max-h-[90vh] overflow-y-auto" role="dialog" aria-modal="true" aria-labelledby="did-doc-title">
|
|
|
|
|
<div class="flex items-center justify-between mb-4">
|
|
|
|
|
<h3 id="did-doc-title" class="text-lg font-semibold text-white">{{ t('web5.didDocument') }}</h3>
|
|
|
|
|
<div class="flex items-center gap-2">
|
|
|
|
|
<span v-if="didDocVerified === true" class="text-xs text-green-400 flex items-center gap-1">
|
|
|
|
|
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
|
|
|
|
|
{{ t('web5.verified') }}
|
|
|
|
|
</span>
|
|
|
|
|
<span v-else-if="didDocVerified === false" class="text-xs text-red-400">{{ t('web5.invalid') }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-if="loadingDidDoc" class="text-white/60 text-sm">{{ t('common.loading') }}</div>
|
|
|
|
|
<pre v-else class="text-xs text-white/80 font-mono bg-black/30 rounded-lg p-4 overflow-x-auto whitespace-pre-wrap">{{ didDocumentFormatted }}</pre>
|
|
|
|
|
<div class="flex gap-3 mt-4">
|
|
|
|
|
<button @click="copyDidDocument" class="flex-1 px-4 py-2 glass-button rounded-lg text-sm font-medium text-white/90 hover:text-white transition-colors">
|
|
|
|
|
{{ didDocCopied ? t('common.copiedBang') : t('common.copy') }}
|
|
|
|
|
</button>
|
|
|
|
|
<button @click="showDidDocModal = false" class="px-4 py-2 rounded-lg bg-white/10 text-white font-medium hover:bg-white/20 transition-colors">
|
|
|
|
|
{{ t('common.close') }}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</Teleport>
|
|
|
|
|
|
2026-04-11 13:35:52 +01:00
|
|
|
<!-- Connected Nodes + Node Visibility -->
|
|
|
|
|
<div class="grid grid-cols-1 xl:grid-cols-2 gap-6 mb-6">
|
|
|
|
|
<Web5ConnectedNodes ref="connectedNodesRef" @toast="showToast" />
|
|
|
|
|
<Web5NodeVisibility :showStagger="showStagger" ref="nodeVisibilityRef" @toast="showToast" />
|
2026-03-21 02:43:28 +00:00
|
|
|
</div>
|
|
|
|
|
|
2026-04-11 13:35:52 +01:00
|
|
|
<!-- Identities + Nostr Relays -->
|
|
|
|
|
<div class="grid grid-cols-1 xl:grid-cols-2 gap-6 mb-6">
|
|
|
|
|
<Web5Identities ref="identitiesRef" :showStagger="showStagger" @toast="showToast" />
|
2026-03-21 02:43:28 +00:00
|
|
|
<Web5NostrRelays ref="nostrRelaysRef" :showStagger="showStagger" />
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-04-11 13:35:52 +01:00
|
|
|
<!-- Monitoring + Federation -->
|
2026-03-21 02:43:28 +00:00
|
|
|
<div class="grid grid-cols-1 xl:grid-cols-2 gap-6 mb-8">
|
2026-04-11 13:35:52 +01:00
|
|
|
<Web5Monitoring />
|
|
|
|
|
<Web5Federation />
|
2026-03-21 02:43:28 +00:00
|
|
|
</div>
|
|
|
|
|
|
2026-04-11 13:35:52 +01:00
|
|
|
<!-- Send/Receive Modals hidden — wallet card removed -->
|
|
|
|
|
<!-- <Web5SendReceiveModals ref="sendReceiveRef" @toast="showToast" @balancesChanged="reloadBalances" /> -->
|
2026-03-21 02:43:28 +00:00
|
|
|
|
|
|
|
|
<!-- Identity Toast -->
|
|
|
|
|
<Transition name="content-fade">
|
|
|
|
|
<div v-if="identityToastVisible" class="fixed bottom-24 md:bottom-8 left-1/2 -translate-x-1/2 z-50 px-4 py-2 rounded-lg bg-black/80 backdrop-blur-md border border-white/10 text-white text-sm shadow-lg">
|
|
|
|
|
{{ identityToastText }}
|
|
|
|
|
</div>
|
|
|
|
|
</Transition>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script lang="ts">
|
|
|
|
|
let web5AnimationDone = false
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
|
|
|
|
import { useRoute } from 'vue-router'
|
|
|
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
|
import { rpcClient } from '@/api/rpc-client'
|
|
|
|
|
import { safeClipboardWrite } from './utils'
|
|
|
|
|
import type { ProfitsData, WalletTransaction, HwWalletDevice } from './types'
|
|
|
|
|
|
|
|
|
|
import Web5QuickActions from './Web5QuickActions.vue'
|
2026-04-11 13:35:52 +01:00
|
|
|
// import Web5Wallet from './Web5Wallet.vue' // hidden for now
|
|
|
|
|
// import Web5Domains from './Web5Domains.vue' // hidden for now
|
2026-03-21 02:43:28 +00:00
|
|
|
import Web5NostrRelays from './Web5NostrRelays.vue'
|
|
|
|
|
import Web5NodeVisibility from './Web5NodeVisibility.vue'
|
|
|
|
|
import Web5ConnectedNodes from './Web5ConnectedNodes.vue'
|
2026-04-11 13:35:52 +01:00
|
|
|
// import Web5SharedContent from './Web5SharedContent.vue' // hidden for now
|
2026-03-21 02:43:28 +00:00
|
|
|
import Web5Identities from './Web5Identities.vue'
|
2026-04-11 13:35:52 +01:00
|
|
|
// import Web5DWN from './Web5DWN.vue' // hidden for now
|
|
|
|
|
// import Web5CredentialsSummary from './Web5CredentialsSummary.vue' // hidden for now
|
|
|
|
|
import Web5Monitoring from './Web5Monitoring.vue'
|
|
|
|
|
import Web5Federation from './Web5Federation.vue'
|
|
|
|
|
// import Web5SendReceiveModals from './Web5SendReceiveModals.vue' // wallet hidden
|
2026-03-21 02:43:28 +00:00
|
|
|
|
|
|
|
|
const route = useRoute()
|
|
|
|
|
const { t } = useI18n()
|
|
|
|
|
|
|
|
|
|
const showStagger = !web5AnimationDone
|
|
|
|
|
|
|
|
|
|
// Child refs
|
2026-04-11 13:35:52 +01:00
|
|
|
// const domainsRef = ref(null) // hidden for now
|
2026-03-21 02:43:28 +00:00
|
|
|
const nostrRelaysRef = ref<InstanceType<typeof Web5NostrRelays> | null>(null)
|
|
|
|
|
const nodeVisibilityRef = ref<InstanceType<typeof Web5NodeVisibility> | null>(null)
|
|
|
|
|
const connectedNodesRef = ref<InstanceType<typeof Web5ConnectedNodes> | null>(null)
|
|
|
|
|
const identitiesRef = ref<InstanceType<typeof Web5Identities> | null>(null)
|
2026-04-11 13:35:52 +01:00
|
|
|
// const dwnRef = ref(null) // hidden for now
|
|
|
|
|
// const credentialsRef = ref(null) // hidden for now
|
|
|
|
|
// const sharedContentRef = ref(null) // hidden for now
|
|
|
|
|
// const sendReceiveRef = ref(null) // wallet hidden
|
2026-03-21 02:43:28 +00:00
|
|
|
|
|
|
|
|
// --- Toast ---
|
|
|
|
|
const identityToastText = ref('')
|
|
|
|
|
const identityToastVisible = ref(false)
|
|
|
|
|
let identityToastTimer: ReturnType<typeof setTimeout> | undefined
|
|
|
|
|
|
|
|
|
|
function showToast(text: string) {
|
|
|
|
|
identityToastText.value = text
|
|
|
|
|
identityToastVisible.value = true
|
|
|
|
|
clearTimeout(identityToastTimer)
|
|
|
|
|
identityToastTimer = setTimeout(() => { identityToastVisible.value = false }, 2000)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Networking Profits ---
|
|
|
|
|
const profitsBreakdown = ref<ProfitsData | null>(null)
|
|
|
|
|
const networkingProfitsDisplay = computed(() => {
|
|
|
|
|
if (!profitsBreakdown.value) return '...'
|
|
|
|
|
const sats = profitsBreakdown.value.total_sats
|
|
|
|
|
if (sats === 0) return '0 sats'
|
|
|
|
|
if (sats < 100000) return `${sats.toLocaleString()} sats`
|
|
|
|
|
const btc = sats / 100_000_000
|
|
|
|
|
return `\u20BF${btc.toFixed(8).replace(/0+$/, '').replace(/\.$/, '')}`
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
async function loadNetworkingProfits() {
|
|
|
|
|
try {
|
|
|
|
|
const res = await rpcClient.call<ProfitsData>({ method: 'wallet.networking-profits' })
|
|
|
|
|
profitsBreakdown.value = res
|
|
|
|
|
} catch {
|
|
|
|
|
profitsBreakdown.value = { total_sats: 0, content_sales_sats: 0, routing_fees_sats: 0 }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- DID State ---
|
|
|
|
|
const storedDid = ref<string | null>(null)
|
|
|
|
|
try {
|
|
|
|
|
storedDid.value = localStorage.getItem('neode_did') || null
|
|
|
|
|
} catch { /* noop */ }
|
|
|
|
|
|
|
|
|
|
const userDid = computed(() => storedDid.value)
|
|
|
|
|
const didStatus = computed<'active' | 'inactive' | 'pending'>(() => userDid.value ? 'active' : 'inactive')
|
|
|
|
|
const creatingDid = ref(false)
|
|
|
|
|
const didCopied = ref(false)
|
|
|
|
|
|
|
|
|
|
// did:dht
|
|
|
|
|
const dhtDid = ref<string | null>(null)
|
|
|
|
|
const publishingDht = ref(false)
|
|
|
|
|
const dhtDidCopied = ref(false)
|
|
|
|
|
try {
|
|
|
|
|
dhtDid.value = localStorage.getItem('neode_dht_did') || null
|
|
|
|
|
} catch { /* noop */ }
|
|
|
|
|
|
|
|
|
|
async function createDID() {
|
|
|
|
|
creatingDid.value = true
|
|
|
|
|
try {
|
|
|
|
|
const res = await rpcClient.call<{ did: string }>({ method: 'identity.create-did' })
|
|
|
|
|
storedDid.value = res.did
|
|
|
|
|
localStorage.setItem('neode_did', res.did)
|
|
|
|
|
} catch {
|
|
|
|
|
if (!crypto.subtle) {
|
|
|
|
|
const randomBytes = new Uint8Array(32)
|
|
|
|
|
crypto.getRandomValues(randomBytes)
|
|
|
|
|
const hex = Array.from(randomBytes).map(b => b.toString(16).padStart(2, '0')).join('')
|
|
|
|
|
const did = `did:key:z${hex}`
|
|
|
|
|
storedDid.value = did
|
|
|
|
|
localStorage.setItem('neode_did', did)
|
|
|
|
|
} else {
|
|
|
|
|
const keyPair = await crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign', 'verify'])
|
|
|
|
|
const exported = await crypto.subtle.exportKey('raw', keyPair.publicKey)
|
|
|
|
|
const bytes = new Uint8Array(exported)
|
|
|
|
|
const hex = Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('')
|
|
|
|
|
const did = `did:key:z${hex}`
|
|
|
|
|
storedDid.value = did
|
|
|
|
|
localStorage.setItem('neode_did', did)
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
creatingDid.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function copyDid() {
|
|
|
|
|
if (!userDid.value) return
|
|
|
|
|
await safeClipboardWrite(userDid.value)
|
|
|
|
|
didCopied.value = true
|
|
|
|
|
setTimeout(() => { didCopied.value = false }, 2000)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function publishDhtDid() {
|
|
|
|
|
publishingDht.value = true
|
|
|
|
|
try {
|
|
|
|
|
const identities = await rpcClient.call<{ identities: Array<{ id: string; is_default: boolean }> }>({ method: 'identity.list' })
|
|
|
|
|
const defaultId = identities.identities?.find((i: { is_default: boolean }) => i.is_default)
|
|
|
|
|
if (!defaultId) return
|
|
|
|
|
const res = await rpcClient.call<{ dht_did: string }>({
|
|
|
|
|
method: 'identity.create-dht-did',
|
|
|
|
|
params: { identity_id: defaultId.id },
|
|
|
|
|
})
|
|
|
|
|
dhtDid.value = res.dht_did
|
|
|
|
|
localStorage.setItem('neode_dht_did', res.dht_did)
|
|
|
|
|
} catch {
|
|
|
|
|
const did = storedDid.value || localStorage.getItem('neode_did')
|
|
|
|
|
if (did) {
|
|
|
|
|
const dhtVersion = did.replace('did:key:', 'did:dht:')
|
|
|
|
|
dhtDid.value = dhtVersion
|
|
|
|
|
localStorage.setItem('neode_dht_did', dhtVersion)
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
publishingDht.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function refreshDhtDid() {
|
|
|
|
|
publishingDht.value = true
|
|
|
|
|
try {
|
|
|
|
|
const identities = await rpcClient.call<{ identities: Array<{ id: string; is_default: boolean }> }>({ method: 'identity.list' })
|
|
|
|
|
const defaultId = identities.identities?.find((i: { is_default: boolean }) => i.is_default)
|
|
|
|
|
if (!defaultId) return
|
|
|
|
|
await rpcClient.call({ method: 'identity.refresh-dht-did', params: { identity_id: defaultId.id } })
|
|
|
|
|
} catch { /* silently ignore */ }
|
|
|
|
|
finally { publishingDht.value = false }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function copyDhtDid() {
|
|
|
|
|
if (!dhtDid.value) return
|
|
|
|
|
await safeClipboardWrite(dhtDid.value)
|
|
|
|
|
dhtDidCopied.value = true
|
|
|
|
|
setTimeout(() => { dhtDidCopied.value = false }, 2000)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DID Document modal
|
|
|
|
|
const showDidDocModal = ref(false)
|
|
|
|
|
const loadingDidDoc = ref(false)
|
|
|
|
|
const didDocumentData = ref<Record<string, unknown> | null>(null)
|
|
|
|
|
const didDocVerified = ref<boolean | null>(null)
|
|
|
|
|
const didDocCopied = ref(false)
|
|
|
|
|
|
|
|
|
|
const didDocumentFormatted = computed(() =>
|
|
|
|
|
didDocumentData.value ? JSON.stringify(didDocumentData.value, null, 2) : ''
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
async function showDidDocument() {
|
|
|
|
|
showDidDocModal.value = true
|
|
|
|
|
loadingDidDoc.value = true
|
|
|
|
|
didDocVerified.value = null
|
|
|
|
|
try {
|
|
|
|
|
const doc = await rpcClient.resolveDid()
|
|
|
|
|
didDocumentData.value = doc
|
|
|
|
|
const verification = await rpcClient.call({
|
|
|
|
|
method: 'identity.verify-did-document',
|
|
|
|
|
params: { document: doc },
|
|
|
|
|
}) as { valid: boolean }
|
|
|
|
|
didDocVerified.value = verification.valid
|
|
|
|
|
} catch (err) {
|
|
|
|
|
if (import.meta.env.DEV) console.error('Failed to load DID Document:', err)
|
|
|
|
|
didDocumentData.value = null
|
|
|
|
|
} finally {
|
|
|
|
|
loadingDidDoc.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function copyDidDocument() {
|
|
|
|
|
if (!didDocumentFormatted.value) return
|
|
|
|
|
await safeClipboardWrite(didDocumentFormatted.value)
|
|
|
|
|
didDocCopied.value = true
|
|
|
|
|
setTimeout(() => { didDocCopied.value = false }, 2000)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Wallet / LND Balances ---
|
|
|
|
|
const walletConnected = ref(false)
|
|
|
|
|
const connectingWallet = ref(false)
|
|
|
|
|
const lndOnchainBalance = ref(0)
|
|
|
|
|
const lndChannelBalance = ref(0)
|
|
|
|
|
const walletError = ref('')
|
|
|
|
|
const ecashBalance = ref(0)
|
|
|
|
|
|
2026-04-11 13:35:52 +01:00
|
|
|
// Transactions — wallet card hidden, but loadTransactions still called for QuickActions walletConnected state
|
2026-03-21 02:43:28 +00:00
|
|
|
const walletTransactions = ref<WalletTransaction[]>([])
|
|
|
|
|
|
|
|
|
|
// Hardware wallets
|
|
|
|
|
const detectedHwWallets = ref<HwWalletDevice[]>([])
|
|
|
|
|
|
|
|
|
|
async function loadLndBalances() {
|
|
|
|
|
try {
|
|
|
|
|
const res = await rpcClient.call<{
|
|
|
|
|
balance_sats: number
|
|
|
|
|
channel_balance_sats: number
|
|
|
|
|
synced_to_chain: boolean
|
|
|
|
|
}>({ method: 'lnd.getinfo' })
|
|
|
|
|
lndOnchainBalance.value = res.balance_sats || 0
|
|
|
|
|
lndChannelBalance.value = res.channel_balance_sats || 0
|
|
|
|
|
walletConnected.value = true
|
|
|
|
|
walletError.value = ''
|
|
|
|
|
} catch (e) {
|
|
|
|
|
walletConnected.value = false
|
|
|
|
|
lndOnchainBalance.value = 0
|
|
|
|
|
lndChannelBalance.value = 0
|
|
|
|
|
walletError.value = e instanceof Error ? e.message : 'Failed to load wallet balances'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function loadEcashBalance() {
|
|
|
|
|
try {
|
|
|
|
|
const res = await rpcClient.call<{ balance_sats: number; token_count: number }>({ method: 'wallet.ecash-balance' })
|
|
|
|
|
ecashBalance.value = res.balance_sats ?? 0
|
|
|
|
|
} catch {
|
|
|
|
|
ecashBalance.value = 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function loadTransactions() {
|
|
|
|
|
try {
|
|
|
|
|
const res = await rpcClient.call<{ transactions: WalletTransaction[]; incoming_pending_count: number }>({ method: 'lnd.gettransactions' })
|
|
|
|
|
walletTransactions.value = res.transactions || []
|
|
|
|
|
walletError.value = ''
|
|
|
|
|
} catch (e) {
|
|
|
|
|
walletTransactions.value = []
|
|
|
|
|
walletError.value = e instanceof Error ? e.message : 'Failed to load transactions'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function connectWallet() {
|
|
|
|
|
if (walletConnected.value) {
|
|
|
|
|
walletConnected.value = false
|
|
|
|
|
} else {
|
|
|
|
|
connectingWallet.value = true
|
|
|
|
|
await loadLndBalances()
|
|
|
|
|
connectingWallet.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function detectHardwareWallets() {
|
|
|
|
|
try {
|
|
|
|
|
const res = await rpcClient.detectUsbDevices()
|
|
|
|
|
detectedHwWallets.value = res.devices || []
|
|
|
|
|
} catch {
|
|
|
|
|
detectedHwWallets.value = []
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 13:35:52 +01:00
|
|
|
// function reloadBalances() { // wallet hidden
|
|
|
|
|
// loadLndBalances()
|
|
|
|
|
// loadEcashBalance()
|
|
|
|
|
// loadTransactions()
|
|
|
|
|
// }
|
2026-03-21 02:43:28 +00:00
|
|
|
|
|
|
|
|
// Auto-refresh wallet data every 30s
|
|
|
|
|
let walletRefreshInterval: ReturnType<typeof setInterval> | null = null
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
web5AnimationDone = true
|
|
|
|
|
|
|
|
|
|
// Load the authoritative node DID from the backend
|
|
|
|
|
rpcClient.getNodeDid().then(res => {
|
|
|
|
|
if (res.did && res.did !== storedDid.value) {
|
|
|
|
|
storedDid.value = res.did
|
|
|
|
|
try { localStorage.setItem('neode_did', res.did) } catch { /* noop */ }
|
|
|
|
|
}
|
|
|
|
|
}).catch(() => { /* use cached localStorage value */ })
|
|
|
|
|
|
|
|
|
|
// Load all data from child components
|
|
|
|
|
connectedNodesRef.value?.loadPeers()
|
|
|
|
|
connectedNodesRef.value?.loadReceivedMessages()
|
|
|
|
|
connectedNodesRef.value?.loadConnectionRequests()
|
|
|
|
|
identitiesRef.value?.loadIdentities()
|
|
|
|
|
nodeVisibilityRef.value?.loadVisibility()
|
2026-04-11 13:35:52 +01:00
|
|
|
// domainsRef.value?.loadDomainNames() // hidden for now
|
2026-03-21 02:43:28 +00:00
|
|
|
nostrRelaysRef.value?.loadNostrRelays()
|
2026-04-11 13:35:52 +01:00
|
|
|
// dwnRef.value?.loadDwnStatus() // hidden for now
|
|
|
|
|
// dwnRef.value?.loadDwnProtocols() // hidden for now
|
|
|
|
|
// credentialsRef.value?.loadCredentials() // hidden for now
|
|
|
|
|
// sharedContentRef.value?.loadContentItems() // hidden for now
|
2026-03-21 02:43:28 +00:00
|
|
|
|
|
|
|
|
// Load local state data
|
|
|
|
|
loadEcashBalance()
|
|
|
|
|
loadNetworkingProfits()
|
|
|
|
|
loadLndBalances()
|
|
|
|
|
loadTransactions()
|
|
|
|
|
detectHardwareWallets()
|
|
|
|
|
|
|
|
|
|
// Shared content loaded by the component itself via expose
|
|
|
|
|
// The SharedContent component manages its own loadContentItems
|
|
|
|
|
|
|
|
|
|
walletRefreshInterval = setInterval(() => {
|
|
|
|
|
loadLndBalances()
|
|
|
|
|
loadTransactions()
|
|
|
|
|
loadEcashBalance()
|
|
|
|
|
}, 30000)
|
|
|
|
|
|
|
|
|
|
// Open Messages tab when navigated via toast
|
|
|
|
|
if (route.query.tab === 'messages') {
|
|
|
|
|
connectedNodesRef.value?.scrollToMessages()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
if (walletRefreshInterval) {
|
|
|
|
|
clearInterval(walletRefreshInterval)
|
|
|
|
|
walletRefreshInterval = null
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
watch(() => route.query.tab, (tab) => {
|
|
|
|
|
if (tab === 'messages') {
|
|
|
|
|
connectedNodesRef.value?.scrollToMessages()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
</script>
|