feat(ui): PeerFiles renders from the shared peer-browse cache (B4)
Demo images / Build & push demo images (push) Failing after 2m11s

- PeerFiles.vue reads the SAME `cloud.peer-browse:<onion>` entry Cloud.vue's
  per-peer fan-in fills, so Cloud → peer files paints instantly from cache
  and revalidates behind it; catalog/error/loading/transport are now
  computed views over the store entry.
- preview-peer fan-out is capped at 3 concurrent with a queue (was one 30s
  RPC per media item, all at once, unbounded) and aborts on unmount.
- browse + preview RPCs drop to maxRetries:1 — retry×3 turned one slow
  peer into a 90s spinner.
- fix useCachedResource's interface types: `ReturnType<typeof computed<T>>`
  resolves to the writable overload (WritableComputedRef), which broke
  vue-tsc against the plain computed() returns; use ComputedRef<T>.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-07-28 04:46:26 -04:00
co-authored by Claude Fable 5
parent a3f07d5ac6
commit d605d0d544
2 changed files with 72 additions and 34 deletions
@@ -22,7 +22,7 @@
// - Abort-on-unmount: the fetcher receives an AbortSignal that fires when
// the last subscribed component unmounts.
import { computed, getCurrentScope, onScopeDispose } from 'vue'
import { computed, getCurrentScope, onScopeDispose, type ComputedRef } from 'vue'
import { useResourcesStore, type ResourceEntry, type ResourceLoadState } from '@/stores/resources'
export interface CachedResourceOptions<T> {
@@ -44,12 +44,12 @@ export interface CachedResourceOptions<T> {
export interface CachedResource<T> {
entry: ResourceEntry<T>
/** Convenience computed views over the entry. */
data: ReturnType<typeof computed<T | null>>
loadState: ReturnType<typeof computed<ResourceLoadState>>
error: ReturnType<typeof computed<string | null>>
data: ComputedRef<T | null>
loadState: ComputedRef<ResourceLoadState>
error: ComputedRef<string | null>
/** True when data exists but is older than the TTL (drive an age badge). */
isStale: ReturnType<typeof computed<boolean>>
ageMs: ReturnType<typeof computed<number | null>>
isStale: ComputedRef<boolean>
ageMs: ComputedRef<number | null>
/** Force a refresh now (deduped with any in-flight one). */
refresh: () => Promise<void>
/** Mark stale + debounce-refresh all mounted users of this key. */