Browse Source

revamp toc for background-loading of the entire publication map

master
silberengel 3 months ago
parent
commit
5f288295fc
  1. 101
      src/lib/components/publications/Publication.svelte
  2. 29
      src/lib/components/publications/TableOfContents.svelte

101
src/lib/components/publications/Publication.svelte

@ -300,6 +300,94 @@
} }
} }
/**
* Background-loads all events in the publication tree in breadth-first order (level by level).
* This ensures the TOC is fully populated with all sections.
*
* Loads: root -> level 1 children -> level 2 children -> etc.
* Also resolves children for each entry to establish parent relationships in TOC.
*
* AI-NOTE: Throttled to avoid blocking main publication loading. Processes in small batches
* with delays to prevent overwhelming relays.
*/
async function backgroundLoadAllEvents() {
if (!publicationTree || !toc) {
console.warn("[Publication] publicationTree or toc is not available for background loading");
return;
}
console.log("[Publication] Starting background load of all events in level-layers (throttled)");
// Throttling configuration
const BATCH_SIZE = 10; // Process 3 addresses at a time
const BATCH_DELAY_MS = 200; // 200ms delay between batches
const LEVEL_DELAY_MS = 500; // 500ms delay between levels
// Track which addresses we've processed to avoid duplicates
const processedAddresses = new Set<string>();
// Start with root address
const queue: string[] = [rootAddress];
processedAddresses.add(rootAddress);
// Process level by level (breadth-first)
while (queue.length > 0) {
const currentLevelAddresses = [...queue];
queue.length = 0; // Clear queue for next level
// Process addresses in small batches to avoid overwhelming relays
for (let i = 0; i < currentLevelAddresses.length; i += BATCH_SIZE) {
const batch = currentLevelAddresses.slice(i, i + BATCH_SIZE);
// Process batch in parallel
const batchPromises = batch.map(async (address) => {
try {
// Get child addresses for this node - this triggers node resolution
const childAddresses = await publicationTree.getChildAddresses(address);
// Resolve children for this entry to establish parent relationships in TOC
const entry = toc.getEntry(address);
if (entry && !entry.childrenResolved) {
await entry.resolveChildren();
}
// Add valid children to queue for next level
for (const childAddress of childAddresses) {
if (childAddress && !processedAddresses.has(childAddress)) {
processedAddresses.add(childAddress);
queue.push(childAddress);
// Resolve the child event to populate TOC (non-blocking)
publicationTree.getEvent(childAddress).catch((error: unknown) => {
console.debug(`[Publication] Error fetching child event ${childAddress}:`, error);
});
}
}
} catch (error) {
console.error(`[Publication] Error loading children for ${address}:`, error);
}
});
// Wait for batch to complete
await Promise.all(batchPromises);
// Small delay between batches to avoid blocking main loading
if (i + BATCH_SIZE < currentLevelAddresses.length) {
await new Promise(resolve => setTimeout(resolve, BATCH_DELAY_MS));
}
}
console.log(`[Publication] Completed level, processed ${currentLevelAddresses.length} addresses, queued ${queue.length} for next level`);
// Delay between levels to give main loading priority
if (queue.length > 0) {
await new Promise(resolve => setTimeout(resolve, LEVEL_DELAY_MS));
}
}
console.log("[Publication] Background load complete, processed", processedAddresses.size, "addresses");
}
// #endregion // #endregion
// AI-NOTE: Combined effect to handle publicationTree changes and initial loading // AI-NOTE: Combined effect to handle publicationTree changes and initial loading
@ -336,6 +424,19 @@
console.log("[Publication] Loading initial content"); console.log("[Publication] Loading initial content");
hasInitialized = true; hasInitialized = true;
loadMore(INITIAL_LOAD_COUNT); loadMore(INITIAL_LOAD_COUNT);
// Start background loading all events in level-layers for TOC
// This runs in the background and doesn't block the UI
// Wait a bit for toc to be initialized
setTimeout(() => {
if (toc && publicationTree) {
backgroundLoadAllEvents().catch((error) => {
console.error("[Publication] Error in background loading:", error);
});
} else {
console.warn("[Publication] toc or publicationTree not available for background loading");
}
}, 100);
}); });
// #region Columns visibility // #region Columns visibility

29
src/lib/components/publications/TableOfContents.svelte

@ -12,7 +12,7 @@
import Self from "./TableOfContents.svelte"; import Self from "./TableOfContents.svelte";
import { onMount, onDestroy } from "svelte"; import { onMount, onDestroy } from "svelte";
let { depth, onSectionFocused, onLoadMore, onClose, toc } = $props<{ let { rootAddress, depth, onSectionFocused, onLoadMore, onClose, toc } = $props<{
rootAddress: string; rootAddress: string;
depth: number; depth: number;
toc: TableOfContents; toc: TableOfContents;
@ -23,11 +23,36 @@
let entries = $derived.by<TocEntry[]>(() => { let entries = $derived.by<TocEntry[]>(() => {
const newEntries = []; const newEntries = [];
const rootEntry = rootAddress === toc.getRootEntry()?.address
? toc.getRootEntry()
: toc.getEntry(rootAddress);
if (!rootEntry) {
return [];
}
// Filter entries that are direct children of rootAddress at the correct depth
for (const [_, entry] of toc.addressMap) { for (const [_, entry] of toc.addressMap) {
// Must match the depth
if (entry.depth !== depth) { if (entry.depth !== depth) {
continue; continue;
} }
// Check if entry is a direct child of rootAddress
// Primary check: parent relationship (set when resolveChildren is called)
// Fallback: entry is in rootEntry's children array
// Final fallback: depth-based check for root's direct children only
const isDirectChild =
entry.parent?.address === rootAddress ||
rootEntry.children.some((child: TocEntry) => child.address === entry.address) ||
(entry.depth === rootEntry.depth + 1 &&
rootAddress === toc.getRootEntry()?.address &&
!entry.parent); // Only use depth check if parent not set (temporary state)
if (!isDirectChild) {
continue;
}
newEntries.push(entry); newEntries.push(entry);
} }

Loading…
Cancel
Save