feat(ui): Federation adopts the cached-resource store (B4)
Some checks failed
Demo images / Build & push demo images (push) Failing after 2m7s
Some checks failed
Demo images / Build & push demo images (push) Failing after 2m7s
federation nodes + dwn.status become useCachedResource entries: revisits paint the node list instantly, `loading` fires on true first-load only (the old showLoader semantics), the 5s poll refreshes silently like the old surfaceErrors:false path, and explicit reloads after mutations still surface failures in the error banner. Replaces the hand-rolled loadNodesWithOptions SWR. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
a969f892ea
commit
1a306c7450
@ -229,6 +229,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { rpcClient } from '@/api/rpc-client'
|
||||
import { useCachedResource } from '@/composables/useCachedResource'
|
||||
import { useTransportStore } from '@/stores/transport'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { useSyncStore } from '@/stores/sync'
|
||||
@ -250,8 +251,15 @@ const transportStore = useTransportStore()
|
||||
const appStore = useAppStore()
|
||||
const syncStore = useSyncStore()
|
||||
|
||||
const nodes = ref<FederatedNode[]>([])
|
||||
const loading = ref(true)
|
||||
// Cached: revisits paint the node list instantly; the 5s poll and mutation
|
||||
// refreshes revalidate behind it. `loading` is initial-load only (background
|
||||
// refreshes keep content on screen — the old showLoader:false semantics).
|
||||
const nodesRes = useCachedResource<FederatedNode[]>({
|
||||
key: 'federation.nodes',
|
||||
fetcher: async () => (await rpcClient.federationListNodes()).nodes,
|
||||
})
|
||||
const nodes = computed(() => nodesRes.data.value ?? [])
|
||||
const loading = computed(() => nodesRes.loadState.value === 'loading')
|
||||
const error = ref('')
|
||||
const selectedNode = ref<FederatedNode | null>(null)
|
||||
const inviteType = ref<'trusted' | 'observer'>('trusted')
|
||||
@ -320,7 +328,12 @@ const mapLinks = computed(() => {
|
||||
}))
|
||||
})
|
||||
|
||||
const dwnStatus = ref<DwnStatus | null>(null)
|
||||
const dwnStatusRes = useCachedResource<DwnStatus>({
|
||||
key: 'federation.dwn-status',
|
||||
fetcher: (signal) => rpcClient.call<DwnStatus>({ method: 'dwn.status', signal, dedup: true, maxRetries: 1 }),
|
||||
immediate: false,
|
||||
})
|
||||
const dwnStatus = computed(() => dwnStatusRes.data.value)
|
||||
const dwnSyncing = ref(false)
|
||||
|
||||
const dwnSyncDotClass = computed(() => {
|
||||
@ -500,25 +513,13 @@ function isOnlineCheck(node: FederatedNode): boolean {
|
||||
return lastSeen > tenMinutesAgo
|
||||
}
|
||||
|
||||
/** Explicit reload (mutations, retry): surfaces a load failure in the error
|
||||
* banner. The background poll calls nodesRes.refresh() directly and stays
|
||||
* silent, like the old surfaceErrors:false path. */
|
||||
async function loadNodes() {
|
||||
return loadNodesWithOptions()
|
||||
}
|
||||
|
||||
async function loadNodesWithOptions(options: { showLoader?: boolean; surfaceErrors?: boolean } = {}) {
|
||||
const showLoader = options.showLoader ?? nodes.value.length === 0
|
||||
const surfaceErrors = options.surfaceErrors ?? true
|
||||
try {
|
||||
if (showLoader) loading.value = true
|
||||
const result = await rpcClient.federationListNodes()
|
||||
nodes.value = result.nodes
|
||||
error.value = ''
|
||||
} catch (e) {
|
||||
if (surfaceErrors) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to load nodes'
|
||||
}
|
||||
} finally {
|
||||
if (showLoader) loading.value = false
|
||||
}
|
||||
await nodesRes.refresh()
|
||||
if (nodesRes.error.value) error.value = nodesRes.error.value
|
||||
else error.value = ''
|
||||
}
|
||||
|
||||
function handleGenerateInvite(type: 'trusted' | 'observer') {
|
||||
@ -610,13 +611,8 @@ async function deployApp(did: string, appId: string) {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadDwnStatus() {
|
||||
try {
|
||||
const result = await rpcClient.call<DwnStatus>({ method: 'dwn.status' })
|
||||
dwnStatus.value = result
|
||||
} catch {
|
||||
dwnStatus.value = null
|
||||
}
|
||||
function loadDwnStatus() {
|
||||
return dwnStatusRes.refresh()
|
||||
}
|
||||
|
||||
async function triggerDwnSync() {
|
||||
@ -681,7 +677,6 @@ async function rotateDid(password: string) {
|
||||
let autoRefreshTimer: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
onMounted(async () => {
|
||||
loadNodesWithOptions({ showLoader: true })
|
||||
loadDwnStatus()
|
||||
loadDiscoveryState()
|
||||
loadPendingRequests()
|
||||
@ -694,7 +689,7 @@ onMounted(async () => {
|
||||
// Self DID not available
|
||||
}
|
||||
autoRefreshTimer = setInterval(() => {
|
||||
loadNodesWithOptions({ showLoader: false, surfaceErrors: false })
|
||||
void nodesRes.refresh()
|
||||
loadPendingRequests()
|
||||
}, 5000)
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user