feat(ui): FIPS network + seed-anchor cards render from cached resources (B4)
Some checks failed
Demo images / Build & push demo images (push) Failing after 2m6s
Some checks failed
Demo images / Build & push demo images (push) Failing after 2m6s
The two FIPS containers on the Server page were the last network cards still fetching-on-mount into local refs — every visit was a blank card until fips.status / fips.list-seed-anchors answered. Both now ride the cached-resource layer: FipsNetworkCard shares the server.fips-summary key with the Local Network card's FIPS row (one fetch, never disagree), seed anchors cache under server.fips-seed-anchors, and mutations write the RPC's authoritative result straight into the cache. The 15s status poll skips hidden tabs — revalidate-on-focus covers the return. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
2971623145
commit
537c52d11c
@ -121,6 +121,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { rpcClient } from '@/api/rpc-client'
|
||||
import { useCachedResource } from '@/composables/useCachedResource'
|
||||
import { safeClipboardWrite } from '@/views/web5/utils'
|
||||
import FipsSeedAnchorsCard from './FipsSeedAnchorsCard.vue'
|
||||
|
||||
@ -136,7 +137,14 @@ interface FipsStatus {
|
||||
anchor_connected?: boolean
|
||||
}
|
||||
|
||||
const status = ref<FipsStatus>({
|
||||
// Shares `server.fips-summary` with the Local Network card's FIPS row, so
|
||||
// both paint from the same cache instantly on revisit and never disagree.
|
||||
const statusRes = useCachedResource<FipsStatus>({
|
||||
key: 'server.fips-summary',
|
||||
ttlMs: 15_000,
|
||||
fetcher: (signal) => rpcClient.call<FipsStatus>({ method: 'fips.status', signal, dedup: true, maxRetries: 1 }),
|
||||
})
|
||||
const status = computed<FipsStatus>(() => statusRes.data.value ?? {
|
||||
installed: false,
|
||||
version: null,
|
||||
service_state: 'unknown',
|
||||
@ -199,18 +207,11 @@ function flash(msg: string, isError = false) {
|
||||
setTimeout(() => { statusMessage.value = '' }, 6000)
|
||||
}
|
||||
|
||||
async function loadStatus() {
|
||||
try {
|
||||
status.value = await rpcClient.call<FipsStatus>({ method: 'fips.status' })
|
||||
} catch (e) {
|
||||
if (import.meta.env.DEV) console.warn('fips.status failed', e)
|
||||
}
|
||||
}
|
||||
|
||||
async function installAndActivate() {
|
||||
installing.value = true
|
||||
try {
|
||||
status.value = await rpcClient.call<FipsStatus>({ method: 'fips.install' })
|
||||
const next = await rpcClient.call<FipsStatus>({ method: 'fips.install' })
|
||||
statusRes.optimistic(() => next) // confirmed server state, not a guess
|
||||
flash('FIPS started')
|
||||
} catch (e: unknown) {
|
||||
const msg = e instanceof Error ? e.message : String(e)
|
||||
@ -235,7 +236,7 @@ async function reconnectAnchor() {
|
||||
}>({ method: 'fips.reconnect', timeout: 60_000 })
|
||||
// Update the card with the post-reconnect status returned by the
|
||||
// backend — avoids an extra status fetch race.
|
||||
status.value = { ...status.value, ...res.after }
|
||||
statusRes.optimistic((cur) => ({ ...(cur ?? status.value), ...res.after }))
|
||||
if (res.recovered) {
|
||||
flash('Anchor reconnected.')
|
||||
} else if (res.likely_cause === 'connected') {
|
||||
@ -258,8 +259,10 @@ async function reconnectAnchor() {
|
||||
// stuck showing whatever anchor state existed at mount time forever.
|
||||
let statusInterval: ReturnType<typeof setInterval> | null = null
|
||||
onMounted(() => {
|
||||
loadStatus()
|
||||
statusInterval = setInterval(loadStatus, 15000)
|
||||
statusInterval = setInterval(() => {
|
||||
if (document.hidden) return
|
||||
void statusRes.refresh()
|
||||
}, 15000)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
if (statusInterval) clearInterval(statusInterval)
|
||||
|
||||
@ -88,8 +88,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import { rpcClient } from '@/api/rpc-client'
|
||||
import { useCachedResource } from '@/composables/useCachedResource'
|
||||
|
||||
defineProps<{ closable?: boolean }>()
|
||||
defineEmits<{ (e: 'close'): void }>()
|
||||
@ -107,7 +108,16 @@ interface ApplyResult {
|
||||
message: string
|
||||
}
|
||||
|
||||
const anchors = ref<SeedAnchor[]>([])
|
||||
const anchorsRes = useCachedResource<SeedAnchor[]>({
|
||||
key: 'server.fips-seed-anchors',
|
||||
fetcher: async (signal) => {
|
||||
const res = await rpcClient.call<{ seed_anchors: SeedAnchor[] }>({
|
||||
method: 'fips.list-seed-anchors', signal, dedup: true, maxRetries: 1,
|
||||
})
|
||||
return res.seed_anchors
|
||||
},
|
||||
})
|
||||
const anchors = computed(() => anchorsRes.data.value ?? [])
|
||||
const adding = ref(false)
|
||||
const applying = ref(false)
|
||||
const statusMessage = ref('')
|
||||
@ -126,15 +136,6 @@ function flash(msg: string, isError = false) {
|
||||
setTimeout(() => { statusMessage.value = '' }, 6000)
|
||||
}
|
||||
|
||||
async function load() {
|
||||
try {
|
||||
const res = await rpcClient.call<{ seed_anchors: SeedAnchor[] }>({ method: 'fips.list-seed-anchors' })
|
||||
anchors.value = res.seed_anchors
|
||||
} catch (e: unknown) {
|
||||
if (import.meta.env.DEV) console.warn('fips.list-seed-anchors failed', e)
|
||||
}
|
||||
}
|
||||
|
||||
async function addAnchor() {
|
||||
if (!draft.npub.trim() || !draft.address.trim()) return
|
||||
adding.value = true
|
||||
@ -148,7 +149,7 @@ async function addAnchor() {
|
||||
label: draft.label.trim(),
|
||||
},
|
||||
})
|
||||
anchors.value = res.seed_anchors
|
||||
anchorsRes.optimistic(() => res.seed_anchors) // authoritative post-add list
|
||||
draft.npub = ''
|
||||
draft.address = ''
|
||||
draft.label = ''
|
||||
@ -168,7 +169,7 @@ async function removeAnchor(npub: string) {
|
||||
method: 'fips.remove-seed-anchor',
|
||||
params: { npub },
|
||||
})
|
||||
anchors.value = res.seed_anchors
|
||||
anchorsRes.optimistic(() => res.seed_anchors)
|
||||
flash('Anchor removed.')
|
||||
} catch (e: unknown) {
|
||||
const msg = e instanceof Error ? e.message : String(e)
|
||||
@ -189,6 +190,4 @@ async function applyAll() {
|
||||
applying.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
</script>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user