refactor(federation): keyed diff-reconciled map scene — stable intro, smooth revisits
Root causes of the flaky intro / janky revisits, all fixed structurally: - Graph updates now RECONCILE against the live scene by identity (peer DID / request id) instead of wiping the SVG: in-place updates for label/online/ trust changes, animated arrivals for new nodes, animated departures for removed ones, and short-way-around slot glides when the ring layout shifts. The old wipe-and-rebuild orphaned every in-flight intro tween whenever the 5s poll or the async self-DID fetch landed mid-intro — that was the 'sometimes parts don't show'. - The intro is gated on readiness: first real container measurement (post- layout, via ResizeObserver) AND the self node being present, with a 1.2s fallback. Graph changes during the intro queue and apply on completion. - Layout is deterministic per node: peers sort by DID for slot assignment and jitter/phase hash from the DID, so backend response order can't shuffle the map between polls. - The long cinematic intro plays once per browser session; revisits get a shorter, lighter entrance that doesn't fight the route transition. - selfDid seeds from the cached localStorage value (re-cached on fetch) so the centre node and links exist from the first frame. - Requests' waiting-room orbit pushed clearly outside the peer rings (+1.0) with loose per-request hover jitter — outside until accepted, then the join animation glides them in. - Full unmount hygiene: every tween killed by target (incl. the repeating sonar pulse), fallback timer cleared, window listeners removed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
6ba24b0f4a
commit
4ffd632506
File diff suppressed because it is too large
Load Diff
@@ -339,7 +339,13 @@ function onMapSelect(did: string) {
|
||||
if (node) selectedNode.value = node
|
||||
}
|
||||
|
||||
const selfDid = ref('')
|
||||
/** Seeded from the cached DID so the map's centre node (and its links) exist
|
||||
* on the very first frame; the authoritative fetch in onMounted refreshes it
|
||||
* and re-caches. Without this the intro raced the RPC and often played with
|
||||
* no centre. */
|
||||
const selfDid = ref<string>((() => {
|
||||
try { return localStorage.getItem('neode_did') || '' } catch { return '' }
|
||||
})())
|
||||
|
||||
const mapNodes = computed(() => {
|
||||
const result = []
|
||||
@@ -819,6 +825,7 @@ onMounted(async () => {
|
||||
try {
|
||||
const result = await rpcClient.getNodeDid()
|
||||
selfDid.value = result.did
|
||||
try { localStorage.setItem('neode_did', result.did) } catch { /* private mode */ }
|
||||
} catch {
|
||||
// Self DID not available
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user