Browse Source

Fix person node toggle persistence issue

- Add hasInitializedPersons flag to track first-time initialization
- Only auto-disable person nodes on first show, not on every update
- Clear disabled persons when person visualizer is hidden
- Update EventTypeConfig to show profile stats in format: [limit] of [total] fetched

This fixes the issue where person nodes would immediately disable after being toggled on when navigating from a publication page.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
master
limina1 9 months ago
parent
commit
d620ff2a4c
  1. 22
      docs/event-types-panel-redesign.org
  2. 26
      src/lib/components/EventTypeConfig.svelte
  3. 11
      src/lib/navigator/EventNetwork/index.svelte
  4. 77
      src/routes/visualize/+page.svelte

22
docs/event-types-panel-redesign.org

@ -16,19 +16,19 @@ Clean implementation plan for the event network visualization, focusing on perfo @@ -16,19 +16,19 @@ Clean implementation plan for the event network visualization, focusing on perfo
* Implementation Phases
** Phase 1: Tag Anchor Controls Migration
- Move tag type selection from Settings to Legend
- Move expansion depth control from Settings to Legend
- Move requirePublications checkbox from Settings to Legend
- Use native HTML button instead of flowbite Toggle component
- Clean up Settings panel
- +Move tag type selection from Settings to Legend+
- +Move expansion depth control from Settings to Legend+
- +Move requirePublications checkbox from Settings to Legend+
- +Use native HTML button instead of flowbite Toggle component+
- +Clean up Settings panel+
** Phase 2: Person Visualizer
- Add collapsible "Person Visualizer" section in Legend
- Display all event authors (pubkeys) as list items
- Fetch display names from kind 0 events
- Render person nodes as diamond shapes in graph
- Default all person nodes to disabled state
- Click to toggle individual person visibility
- +Add collapsible "Person Visualizer" section in Legend+
- +Display all event authors (pubkeys) as list items+
- +Fetch display names from kind 0 events+
- +Render person nodes as diamond shapes in graph+
- +Default all person nodes to disabled state+
- +Click to toggle individual person visibility+
** Phase 3: State Management Fixes
- Replace reactive Set with object/map for disabled states

26
src/lib/components/EventTypeConfig.svelte

@ -6,10 +6,12 @@ @@ -6,10 +6,12 @@
let {
onReload = () => {},
eventCounts = {}
eventCounts = {},
profileStats = { totalFetched: 0, displayLimit: 50 }
} = $props<{
onReload?: () => void;
eventCounts?: { [kind: number]: number };
profileStats?: { totalFetched: number; displayLimit: number };
}>();
let newKind = $state('');
@ -122,7 +124,22 @@ @@ -122,7 +124,22 @@
<CloseCircleOutline class="w-4 h-4" />
</button>
<!-- Limit input for all kinds -->
<!-- Special format for kind 0 (profiles) -->
{#if config.kind === 0}
<input
type="number"
value={profileStats.displayLimit}
min="1"
max={profileStats.totalFetched || 1000}
class="w-16 px-2 py-1 text-xs border rounded dark:bg-gray-700 dark:border-gray-600 dark:text-white"
oninput={(e) => handleLimitChange(config.kind, e.currentTarget.value)}
title="Max profiles to display"
/>
<span class="text-xs text-gray-600 dark:text-gray-400">
of {profileStats.totalFetched} fetched
</span>
{:else}
<!-- Limit input for other kinds -->
<input
type="number"
value={config.limit}
@ -132,6 +149,7 @@ @@ -132,6 +149,7 @@
oninput={(e) => handleLimitChange(config.kind, e.currentTarget.value)}
title="Max to display"
/>
{/if}
<!-- Nested levels for 30040 -->
{#if config.kind === 30040}
@ -162,11 +180,11 @@ @@ -162,11 +180,11 @@
{/if}
<!-- Load indicator -->
{#if isLoaded}
{#if config.kind !== 0 && isLoaded}
<span class="text-xs text-green-600 dark:text-green-400">
({eventCounts[config.kind]})
</span>
{:else}
{:else if config.kind !== 0}
<span class="text-xs text-red-600 dark:text-red-400">
(not loaded)
</span>

11
src/lib/navigator/EventNetwork/index.svelte

@ -165,6 +165,7 @@ @@ -165,6 +165,7 @@
let personMap = $state<Map<string, any>>(new Map());
let totalPersonCount = $state(0);
let displayedPersonCount = $state(0);
let hasInitializedPersons = $state(false);
// Debug function - call from browser console: window.debugTagAnchors()
if (typeof window !== "undefined") {
@ -357,13 +358,14 @@ @@ -357,13 +358,14 @@
// Extract person info for legend
personAnchorInfo = extractPersonAnchorInfo(personAnchors, personMap);
// Auto-disable all person nodes by default (only on first show)
if (disabledPersons.size === 0) {
// Auto-disable all person nodes by default (only on first time showing)
if (!hasInitializedPersons && personAnchors.length > 0) {
personAnchors.forEach(anchor => {
if (anchor.pubkey) {
disabledPersons.add(anchor.pubkey);
}
});
hasInitializedPersons = true;
}
debug("Person anchors created", {
@ -374,6 +376,11 @@ @@ -374,6 +376,11 @@
});
} else {
personAnchorInfo = [];
// Reset initialization flag when person nodes are hidden
if (hasInitializedPersons && personAnchorInfo.length === 0) {
hasInitializedPersons = false;
disabledPersons.clear();
}
}
// Save current node positions before updating

77
src/routes/visualize/+page.svelte

@ -237,11 +237,15 @@ @@ -237,11 +237,15 @@
let allFetchedEvents: NDKEvent[] = [];
// First, fetch non-publication events (like kind 0, 1, 3, etc.)
if (otherConfigs.length > 0) {
debug("Fetching non-publication events:", otherConfigs);
// First, fetch non-publication events (like kind 1, 3, etc. but NOT kind 0)
// We'll fetch kind 0 profiles after we know which pubkeys we need
const kind0Config = otherConfigs.find(c => c.kind === 0);
const nonProfileConfigs = otherConfigs.filter(c => c.kind !== 0);
for (const config of otherConfigs) {
if (nonProfileConfigs.length > 0) {
debug("Fetching non-publication events (excluding profiles):", nonProfileConfigs);
for (const config of nonProfileConfigs) {
try {
// Special handling for kind 3 (follow lists)
if (config.kind === 3) {
@ -483,26 +487,16 @@ @@ -483,26 +487,16 @@
baseEvents = [...allEvents]; // Store base events for tag expansion
// Step 6: Fetch profiles (kind 0)
debug("Fetching profiles for events");
// Get kind 0 config to respect its limit
const profileConfig = enabledConfigs.find(ec => ec.kind === 0);
const profileLimit = profileConfig?.limit || 50;
// Step 6: Extract all pubkeys and fetch profiles
debug("Extracting pubkeys from all events");
// Collect all pubkeys that need profiles
const allPubkeys = new Set<string>();
// Add event authors (these are the main content creators)
allEvents.forEach(event => {
if (event.pubkey) {
allPubkeys.add(event.pubkey);
}
});
// Use the utility function to extract ALL pubkeys (authors + p tags + content)
const allPubkeys = extractPubkeysFromEvents(allEvents);
// Add pubkeys from follow lists (for tag anchors)
// Add pubkeys from follow lists if present
if (followListEvents.length > 0) {
followListEvents.forEach(event => {
if (event.pubkey) allPubkeys.add(event.pubkey);
event.tags.forEach(tag => {
if (tag[0] === 'p' && tag[1]) {
allPubkeys.add(tag[1]);
@ -511,25 +505,38 @@ @@ -511,25 +505,38 @@
});
}
// Limit the number of profiles to fetch based on kind 0 limit
const pubkeysArray = Array.from(allPubkeys);
const pubkeysToFetch = profileLimit === -1
? pubkeysArray
: pubkeysArray.slice(0, profileLimit);
debug("Profile fetch strategy:", {
debug("Profile extraction complete:", {
totalPubkeys: allPubkeys.size,
profileLimit,
pubkeysToFetch: pubkeysToFetch.length,
followListsLoaded: followListEvents.length
fromEvents: allEvents.length,
fromFollowLists: followListEvents.length
});
profileLoadingProgress = { current: 0, total: pubkeysToFetch.length };
await batchFetchProfiles(pubkeysToFetch, (fetched, total) => {
// Fetch ALL profiles if kind 0 is enabled
if (kind0Config) {
debug("Fetching profiles for all discovered pubkeys");
// Update progress during fetch
profileLoadingProgress = { current: 0, total: allPubkeys.size };
await batchFetchProfiles(
Array.from(allPubkeys),
(fetched, total) => {
profileLoadingProgress = { current: fetched, total };
});
profileLoadingProgress = null; // Clear progress when done
debug("Profile fetch complete for", pubkeysToFetch.length, "pubkeys");
}
);
profileLoadingProgress = null;
debug("Profile fetch complete");
// Store the total count for display
// The limit in kind0Config now controls display, not fetch
if (typeof window !== 'undefined' && window.profileStats) {
window.profileStats = {
totalFetched: allPubkeys.size,
displayLimit: kind0Config.limit
};
}
}
// Step 7: Apply display limits
events = filterByDisplayLimits(allEvents, $displayLimits, $visualizationConfig);

Loading…
Cancel
Save