feat(ui): Cloud page renders from cache — per-peer incremental fan-in, live FIPS/Tor badges, per-path folder cache
Demo images / Build & push demo images (push) Failing after 3m24s
Demo images / Build & push demo images (push) Failing after 3m24s
Part B3 of docs/FIPS-UPTIME-AND-UI-STATE-PLAN.md — the worst fetch-on-every-navigation offender converted to the cached-resource layer: - section counts / peer nodes / my files / paid items are cached resources: revisits paint instantly, refresh happens behind the content (sticky-ready), errors keep last-known data - peer files: per-peer cached browse entries replace the all-or-nothing Promise.allSettled — each peer's rows render the moment it answers, with "still fetching from N peers" + unreachable counts; browse-peer runs with maxRetries:1 so one dead peer costs its timeout once, not ×3 - peer cards get a live transport badge (FIPS green / Tor amber, with measured latency) from the transport field the browse response already carried — the per-peer FIPS-uptime view, for free - cloud store: per-path listing cache with stale-while-revalidate navigate() and a last-wins guard; CloudFolder no longer reset()s the store on every folder entry (that wipe forced a spinner each time) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
67454974b2
commit
c83bade022
@@ -8,6 +8,11 @@ export const useCloudStore = defineStore('cloud', () => {
|
||||
const loading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
const authenticated = ref(false)
|
||||
// Per-path listing cache: re-entering a folder paints the last listing
|
||||
// immediately (no spinner) while the fresh listing loads behind it.
|
||||
const pathCache = new Map<string, FileBrowserItem[]>()
|
||||
// Last-wins guard for overlapping navigations (fast folder hopping).
|
||||
let navSeq = 0
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
const parts = currentPath.value.split('/').filter(Boolean)
|
||||
@@ -36,7 +41,22 @@ export const useCloudStore = defineStore('cloud', () => {
|
||||
}
|
||||
|
||||
async function navigate(path: string): Promise<void> {
|
||||
loading.value = true
|
||||
const seq = ++navSeq
|
||||
const apply = (p: string, result: FileBrowserItem[]) => {
|
||||
pathCache.set(p, result)
|
||||
if (seq !== navSeq) return // a newer navigation superseded this one
|
||||
items.value = result
|
||||
currentPath.value = p
|
||||
}
|
||||
// Stale-while-revalidate: show the cached listing for this path
|
||||
// immediately (no spinner), then refresh it underneath.
|
||||
const cached = pathCache.get(path)
|
||||
if (cached) {
|
||||
items.value = cached
|
||||
currentPath.value = path
|
||||
} else {
|
||||
loading.value = true
|
||||
}
|
||||
error.value = null
|
||||
try {
|
||||
if (!authenticated.value) {
|
||||
@@ -47,9 +67,7 @@ export const useCloudStore = defineStore('cloud', () => {
|
||||
}
|
||||
}
|
||||
try {
|
||||
const result = await fileBrowserClient.listDirectory(path)
|
||||
items.value = result
|
||||
currentPath.value = path
|
||||
apply(path, await fileBrowserClient.listDirectory(path))
|
||||
} catch {
|
||||
// Directory may not exist — try to create it, then retry
|
||||
if (path !== '/') {
|
||||
@@ -57,23 +75,20 @@ export const useCloudStore = defineStore('cloud', () => {
|
||||
const parentPath = path.substring(0, path.lastIndexOf('/')) || '/'
|
||||
const dirName = path.substring(path.lastIndexOf('/') + 1)
|
||||
await fileBrowserClient.createFolder(parentPath, dirName)
|
||||
const result = await fileBrowserClient.listDirectory(path)
|
||||
items.value = result
|
||||
currentPath.value = path
|
||||
apply(path, await fileBrowserClient.listDirectory(path))
|
||||
} catch {
|
||||
// Fall back to root
|
||||
const result = await fileBrowserClient.listDirectory('/')
|
||||
items.value = result
|
||||
currentPath.value = '/'
|
||||
apply('/', await fileBrowserClient.listDirectory('/'))
|
||||
}
|
||||
} else {
|
||||
throw new Error('Failed to list root directory')
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to load files'
|
||||
// Keep showing the cached listing on a failed revalidate.
|
||||
if (!cached) error.value = e instanceof Error ? e.message : 'Failed to load files'
|
||||
} finally {
|
||||
loading.value = false
|
||||
if (seq === navSeq) loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,6 +127,7 @@ export const useCloudStore = defineStore('cloud', () => {
|
||||
items.value = []
|
||||
loading.value = false
|
||||
error.value = null
|
||||
pathCache.clear()
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user