You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3.6 KiB
3.6 KiB
Navigation Visualization Clean Implementation Plan
Overview
Clean implementation plan for the event network visualization, focusing on performance and stability.
Core Principles
- Load once, render many: Fetch all data upfront, toggle visibility without re-fetching
- Simple state management: Avoid reactive Sets and circular dependencies
- Batched operations: Minimize network requests by combining queries
- Clean separation: UI controls in Legend, visualization logic in index.svelte
Implementation Phases
Phase 1: Tag Anchor Controls Migration
Move tag type selection from Settings to LegendMove expansion depth control from Settings to LegendMove requirePublications checkbox from Settings to LegendUse native HTML button instead of flowbite Toggle componentClean up Settings panel
Phase 2: Person Visualizer
Add collapsible "Person Visualizer" section in LegendDisplay all event authors (pubkeys) as list itemsFetch display names from kind 0 eventsRender person nodes as diamond shapes in graphDefault all person nodes to disabled stateClick to toggle individual person visibility
Phase 3: State Management Fixes
- Replace reactive Set with object/map for disabled states
- Use $derived for computed values to avoid circular updates
- Defer state updates with setTimeout where needed
- Simplify $effect dependencies
- Ensure clean data flow without loops
Phase 4: Fetch Optimization
- Batch multiple event kinds into single queries
- Combine 30041 and 30818 content fetches
- Pre-fetch all person profiles on initial load
- Cache profile data to avoid re-fetching
Phase 5: Load-Once Architecture
Fetch ALL configured event kinds upfront (regardless of enabled state)Store complete dataset in memoryOnly render nodes that are enabledToggle operations just change visibility, no re-fetchPrevents UI freezing on toggle operations
Technical Details
State Structure
// Avoid Sets for reactive state
let disabledTagsMap = $state<Record<string, boolean>>({});
let disabledPersonsMap = $state<Record<string, boolean>>({});
// Derived for compatibility
const disabledTags = $derived(new Set(Object.keys(disabledTagsMap).filter(k => disabledTagsMap[k])));
const disabledPersons = $derived(new Set(Object.keys(disabledPersonsMap).filter(k => disabledPersonsMap[k])));
Person Node Structure
interface PersonAnchor extends NetworkNode {
type: "PersonAnchor";
isPersonAnchor: true;
pubkey: string;
displayName?: string;
}
Batch Fetch Example
// Instead of separate queries
const contentEvents = await $ndkInstance.fetchEvents({
kinds: [30041, 30818], // Batch multiple kinds
"#d": Array.from(dTags),
limit: combinedLimit
});
Benefits
- Performance: No re-fetching on toggle operations
- Stability: Avoids infinite loops and reactive state issues
- UX: Smooth, instant toggle without freezing
- Maintainability: Clear separation of concerns
- Scalability: Handles large numbers of nodes efficiently
Additional Improvements
Profile Fetching Optimization
- When follow list limit is 0, only fetch profiles from event authors
- Excludes follow list pubkeys from profile fetching when not needed
- Reduces unnecessary network requests
Person Node Visual Distinction
- Green diamonds (#10B981) for authors of displayed events
- Kind 3 color for people from follow lists
- Visual clarity on social graph relationships
- Legend updates to match graph coloring