Browse Source

remove tag expansion depth

master
limina1 8 months ago
parent
commit
d094aa1277
  1. 4
      src/lib/navigator/EventNetwork/index.svelte
  2. 257
      src/routes/visualize/+page.svelte

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

@ -82,7 +82,7 @@ @@ -82,7 +82,7 @@
totalCount?: number;
onupdate: () => void;
onclear?: () => void;
onTagExpansionChange?: (depth: number, tags: string[]) => void;
onTagExpansionChange?: (tags: string[]) => void;
profileStats?: { totalFetched: number; displayLimit: number };
allEventCounts?: { [kind: number]: number };
}>();
@ -1022,7 +1022,7 @@ @@ -1022,7 +1022,7 @@
tagTypeChanged
});
onTagExpansionChange(0, Array.from(tags));
onTagExpansionChange(Array.from(tags));
}
});

257
src/routes/visualize/+page.svelte

@ -18,6 +18,15 @@ @@ -18,6 +18,15 @@
import { getEventKindColor, getEventKindName } from "$lib/utils/eventColors";
import { extractPubkeysFromEvents, batchFetchProfiles } from "$lib/utils/profileCache";
import { activePubkey } from "$lib/ndk";
// Import utility functions for tag-based event fetching
// These functions handle the complex logic of finding publications by tags
// and extracting their associated content events
import {
fetchTaggedEventsFromRelays,
findTaggedEventsInFetched,
fetchProfilesForNewEvents
} from "$lib/utils/tag_event_fetch";
import { deduplicateAndCombineEvents } from "$lib/utils/eventDeduplication";
// Configuration
const DEBUG = true; // Set to true to enable debug logging
@ -589,13 +598,44 @@ @@ -589,13 +598,44 @@
}
/**
* Updates final state after tag expansion (display limits, missing events)
*
* @param newPublications Array of new publication events
* @param newContentEvents Array of new content events
*/
function updateFinalState(newPublications: NDKEvent[], newContentEvents: NDKEvent[]) {
// Apply display limits
events = filterByDisplayLimits(allEvents, $visualizationConfig);
// Update missing events detection
const eventIds = new Set(allEvents.map(e => e.id));
missingEventIds = detectMissingEvents(events, eventIds);
debug("Events after expansion:", {
base: baseEvents.length,
newPubs: newPublications.length,
newContent: newContentEvents.length,
totalFetched: allEvents.length,
displayed: events.length,
missing: missingEventIds.size,
searchMode: $visualizationConfig.searchThroughFetched ? "fetched" : "relays"
});
}
/**
* Handles tag expansion to fetch related publications
*
* REFACTORED: This function has been broken down into smaller, focused steps:
* 1. Fetch/find tagged events using utility functions
* 2. Deduplicate events by coordinate using utility function
* 3. Fetch profiles for new events using utility function
* 4. Update final state (display limits, missing events)
*/
async function handleTagExpansion(depth: number, tags: string[]) {
debug("Handling tag expansion", { depth, tags, searchThroughFetched: $visualizationConfig.searchThroughFetched });
async function handleTagExpansion(tags: string[]) {
debug("Handling tag expansion", { tags, searchThroughFetched: $visualizationConfig.searchThroughFetched });
if (depth === 0 || tags.length === 0) {
if (tags.length === 0) {
// Reset to base events only
allEvents = [...baseEvents];
events = filterByDisplayLimits(allEvents, $visualizationConfig);
@ -612,192 +652,47 @@ @@ -612,192 +652,47 @@
let newPublications: NDKEvent[] = [];
let newContentEvents: NDKEvent[] = [];
// Step 1: Fetch or find tagged events using utility functions
if ($visualizationConfig.searchThroughFetched) {
// Search through already fetched events only
debug("Searching through already fetched events for tags:", tags);
// Find publications in allEvents that have the specified tags
const taggedPublications = allEvents.filter(event => {
if (event.kind !== INDEX_EVENT_KIND) return false;
if (existingEventIds.has(event.id)) return false; // Skip base events
// Check if event has any of the specified tags
const eventTags = event.getMatchingTags("t").map(tag => tag[1]);
return tags.some(tag => eventTags.includes(tag));
});
newPublications = taggedPublications;
debug("Found", newPublications.length, "publications in fetched events");
// For content events, also search in allEvents
const existingContentDTags = new Set(
baseEvents
.filter(e => e.kind !== undefined && CONTENT_EVENT_KINDS.includes(e.kind))
.map(e => e.tagValue("d"))
.filter(d => d !== undefined)
const result = findTaggedEventsInFetched(
allEvents,
tags,
existingEventIds,
baseEvents,
debug
);
const contentEventDTags = new Set<string>();
newPublications.forEach((event) => {
const aTags = event.getMatchingTags("a");
aTags.forEach((tag) => {
// Parse the 'a' tag identifier: kind:pubkey:d-tag
if (tag[1]) {
const parts = tag[1].split(':');
if (parts.length >= 3) {
const dTag = parts.slice(2).join(':'); // Handle d-tags with colons
if (!existingContentDTags.has(dTag)) {
contentEventDTags.add(dTag);
}
}
}
});
});
// Find content events in allEvents
newContentEvents = allEvents.filter(event => {
if (!CONTENT_EVENT_KINDS.includes(event.kind || 0)) return false;
const dTag = event.tagValue("d");
return dTag !== undefined && contentEventDTags.has(dTag);
});
newPublications = result.publications;
newContentEvents = result.contentEvents;
} else {
// Fetch from relays as before
debug("Fetching from relays for tags:", tags);
// Fetch publications that have any of the specified tags
const taggedPublications = await $ndkInstance.fetchEvents({
kinds: [INDEX_EVENT_KIND],
"#t": tags, // Match any of these tags
limit: 30 * depth // Reasonable limit based on depth
});
debug("Found tagged publications from relays:", taggedPublications.size);
// Filter to avoid duplicates
newPublications = Array.from(taggedPublications).filter(
event => !existingEventIds.has(event.id)
// Fetch from relays using the utility function
const result = await fetchTaggedEventsFromRelays(
tags,
existingEventIds,
baseEvents,
debug
);
// Extract content event d-tags from new publications
const contentEventDTags = new Set<string>();
const existingContentDTags = new Set(
baseEvents
.filter(e => e.kind !== undefined && CONTENT_EVENT_KINDS.includes(e.kind))
.map(e => e.tagValue("d"))
.filter(d => d !== undefined)
);
newPublications.forEach((event) => {
const aTags = event.getMatchingTags("a");
aTags.forEach((tag) => {
// Parse the 'a' tag identifier: kind:pubkey:d-tag
if (tag[1]) {
const parts = tag[1].split(':');
if (parts.length >= 3) {
const dTag = parts.slice(2).join(':'); // Handle d-tags with colons
if (!existingContentDTags.has(dTag)) {
contentEventDTags.add(dTag);
}
}
}
});
});
// Fetch the content events
if (contentEventDTags.size > 0) {
const contentEventsSet = await $ndkInstance.fetchEvents({
kinds: CONTENT_EVENT_KINDS,
"#d": Array.from(contentEventDTags), // Use d-tag filter
});
newContentEvents = Array.from(contentEventsSet);
}
newPublications = result.publications;
newContentEvents = result.contentEvents;
}
// Combine all events with coordinate-based deduplication
// First, build coordinate map for replaceable events
const coordinateMap = new Map<string, NDKEvent>();
const allEventsToProcess = [...baseEvents, ...newPublications, ...newContentEvents];
// First pass: identify the most recent version of each replaceable event
allEventsToProcess.forEach(event => {
if (!event.id) return;
// For replaceable events (30000-39999), track by coordinate
if (event.kind && event.kind >= 30000 && event.kind < 40000) {
const dTag = event.tagValue("d");
const author = event.pubkey;
if (dTag && author) {
const coordinate = `${event.kind}:${author}:${dTag}`;
const existing = coordinateMap.get(coordinate);
// Keep the most recent version
if (!existing || (event.created_at && existing.created_at && event.created_at > existing.created_at)) {
coordinateMap.set(coordinate, event);
}
}
}
});
// Second pass: build final event map
const finalEventMap = new Map<string, NDKEvent>();
const seenCoordinates = new Set<string>();
allEventsToProcess.forEach(event => {
if (!event.id) return;
// For replaceable events, only add if it's the chosen version
if (event.kind && event.kind >= 30000 && event.kind < 40000) {
const dTag = event.tagValue("d");
const author = event.pubkey;
if (dTag && author) {
const coordinate = `${event.kind}:${author}:${dTag}`;
const chosenEvent = coordinateMap.get(coordinate);
// Only add this event if it's the chosen one for this coordinate
if (chosenEvent && chosenEvent.id === event.id && !seenCoordinates.has(coordinate)) {
finalEventMap.set(event.id, event);
seenCoordinates.add(coordinate);
}
return;
}
}
// Non-replaceable events are added directly
finalEventMap.set(event.id, event);
});
allEvents = Array.from(finalEventMap.values());
// Fetch profiles for new events
const newPubkeys = extractPubkeysFromEvents([...newPublications, ...newContentEvents]);
if (newPubkeys.size > 0) {
debug("Fetching profiles for", newPubkeys.size, "new pubkeys from tag expansion");
profileLoadingProgress = { current: 0, total: newPubkeys.size };
await batchFetchProfiles(Array.from(newPubkeys), (fetched, total) => {
profileLoadingProgress = { current: fetched, total };
});
profileLoadingProgress = null;
}
// Step 2: Deduplicate events by coordinate using existing utility function
allEvents = deduplicateAndCombineEvents(
baseEvents, // nonPublicationEvents
new Set(newPublications), // validIndexEvents
new Set(newContentEvents) // contentEvents
);
// Apply display limits
events = filterByDisplayLimits(allEvents, $visualizationConfig);
// Step 3: Fetch profiles for new events using utility function
await fetchProfilesForNewEvents(
newPublications,
newContentEvents,
(progress) => { profileLoadingProgress = progress; },
debug
);
// Update missing events detection
const eventIds = new Set(allEvents.map(e => e.id));
missingEventIds = detectMissingEvents(events, eventIds);
debug("Events after expansion:", {
base: baseEvents.length,
newPubs: newPublications.length,
newContent: newContentEvents.length,
totalFetched: allEvents.length,
displayed: events.length,
missing: missingEventIds.size,
searchMode: $visualizationConfig.searchThroughFetched ? "fetched" : "relays"
});
// Step 4: Update final state (display limits, missing events)
updateFinalState(newPublications, newContentEvents);
} catch (e) {
console.error("Error expanding tags:", e);
@ -953,7 +848,7 @@ @@ -953,7 +848,7 @@
<button
type="button"
class="text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 mt-2 dark:bg-red-600 dark:hover:bg-red-700 focus:outline-none dark:focus:ring-red-800"
on:click={fetchEvents}
onclick={fetchEvents}
>
Retry
</button>

Loading…
Cancel
Save