From 12cf16b36d7afd6f0e6689d1fb8088836b119288 Mon Sep 17 00:00:00 2001 From: silberengel Date: Sun, 3 Aug 2025 22:04:01 +0200 Subject: [PATCH] fixed the publication loading --- src/lib/utils.ts | 21 +++++ src/lib/utils/websocket_utils.ts | 36 ++++---- .../[type]/[identifier]/+layout.server.ts | 39 ++++----- .../[type]/[identifier]/+layout.svelte | 2 + .../[type]/[identifier]/+page.svelte | 10 ++- .../publication/[type]/[identifier]/+page.ts | 82 ++++++++++++++----- 6 files changed, 127 insertions(+), 63 deletions(-) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 2171d53..ee44929 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -2,6 +2,7 @@ import type { NDKEvent } from "@nostr-dev-kit/ndk"; import { nip19 } from "nostr-tools"; import { getMatchingTags } from "./utils/nostrUtils.ts"; import type { AddressPointer, EventPointer } from "nostr-tools/nip19"; +import type { NostrEvent } from "./utils/websocket_utils.ts"; export class DecodeError extends Error { constructor(message: string) { @@ -40,6 +41,26 @@ export function naddrEncode(event: NDKEvent, relays: string[]) { }); } +/** + * Creates a tag address from a raw Nostr event (for compatibility with NDK events) + * @param event The raw Nostr event + * @param relays Optional relay list for the address + * @returns A tag address string + */ +export function createTagAddress(event: NostrEvent, relays: string[] = []): string { + const dTag = event.tags.find((tag: string[]) => tag[0] === "d")?.[1]; + if (!dTag) { + throw new Error("Event does not have a d tag"); + } + + return nip19.naddrEncode({ + identifier: dTag, + pubkey: event.pubkey, + kind: event.kind, + relays, + }); +} + export function nprofileEncode(pubkey: string, relays: string[]) { return nip19.nprofileEncode({ pubkey, relays }); } diff --git a/src/lib/utils/websocket_utils.ts b/src/lib/utils/websocket_utils.ts index 659580e..3d6b608 100644 --- a/src/lib/utils/websocket_utils.ts +++ b/src/lib/utils/websocket_utils.ts @@ -152,26 +152,25 @@ export async function fetchNostrEvent(filter: NostrFilter): Promise((resolve) => { - setTimeout(() => { - console.warn("[WebSocket Utils]: Fetch timeout reached"); - resolve(null); - }, 5000); // 5 second timeout for the entire fetch operation - }); - - // Race between individual relay results and the timeout - const result = await Promise.race([ - // Wait for the first successful result from any relay - Promise.race(relayPromises.filter(p => p !== null)), - timeoutPromise - ]); + // Wait for all relay results and find the first successful one + const results = await Promise.allSettled(relayPromises); - if (result) { - return result; + // Find the first successful result + for (const result of results) { + if (result.status === 'fulfilled' && result.value) { + console.debug(`[WebSocket Utils]: Returning successful result from relay`); + return result.value; + } } - console.warn("[WebSocket Utils]: Failed to fetch event from all relays (timeout or no results)"); + // Debug: log all results to see what happened + console.debug(`[WebSocket Utils]: All relay results:`, results.map((r, i) => ({ + relay: availableRelays[i], + status: r.status, + value: r.status === 'fulfilled' ? r.value : r.reason + }))); + + console.warn("[WebSocket Utils]: Failed to fetch event from all relays (no successful results)"); return null; } @@ -222,12 +221,15 @@ export async function fetchEventByNaddr(naddr: string): Promise { authors: [decoded.pubkey], "#d": [decoded.identifier], }; + console.debug(`[fetchEventByNaddr] Calling fetchNostrEvent with filter:`, filter); const event = await fetchNostrEvent(filter); + console.debug(`[fetchEventByNaddr] fetchNostrEvent returned:`, event ? 'success' : 'null'); if (!event) { error(404, `Event not found for naddr: ${naddr}. href="/events?id=${naddr}"`); } return event; } catch (err) { + console.error(`[fetchEventByNaddr] Error:`, err); if (err && typeof err === "object" && "status" in err) { throw err; } diff --git a/src/routes/publication/[type]/[identifier]/+layout.server.ts b/src/routes/publication/[type]/[identifier]/+layout.server.ts index 26e28b4..2a90624 100644 --- a/src/routes/publication/[type]/[identifier]/+layout.server.ts +++ b/src/routes/publication/[type]/[identifier]/+layout.server.ts @@ -1,40 +1,29 @@ import { error } from "@sveltejs/kit"; import type { LayoutServerLoad } from "./$types"; -import { fetchEventByDTag, fetchEventById, fetchEventByNaddr, fetchEventByNevent } from "../../../../lib/utils/websocket_utils.ts"; import type { NostrEvent } from "../../../../lib/utils/websocket_utils.ts"; +// AI-NOTE: Server-side event fetching for SEO metadata +async function fetchEventServerSide(type: string, identifier: string): Promise { + // For now, return null to indicate server-side fetch not implemented + // This will fall back to client-side fetching + return null; +} + export const load: LayoutServerLoad = async ({ params, url }) => { const { type, identifier } = params; - let indexEvent: NostrEvent; - - // Handle different identifier types - switch (type) { - case 'id': - indexEvent = await fetchEventById(identifier); - break; - case 'd': - indexEvent = await fetchEventByDTag(identifier); - break; - case 'naddr': - indexEvent = await fetchEventByNaddr(identifier); - break; - case 'nevent': - indexEvent = await fetchEventByNevent(identifier); - break; - default: - error(400, `Unsupported identifier type: ${type}`); - } + // Try to fetch event server-side for metadata + const indexEvent = await fetchEventServerSide(type, identifier); - // Extract metadata for meta tags - const title = indexEvent.tags.find((tag) => tag[0] === "title")?.[1] || "Alexandria Publication"; - const summary = indexEvent.tags.find((tag) => tag[0] === "summary")?.[1] || + // Extract metadata for meta tags (use fallbacks if no event found) + const title = indexEvent?.tags.find((tag) => tag[0] === "title")?.[1] || "Alexandria Publication"; + const summary = indexEvent?.tags.find((tag) => tag[0] === "summary")?.[1] || "Alexandria is a digital library, utilizing Nostr events for curated publications and wiki pages."; - const image = indexEvent.tags.find((tag) => tag[0] === "image")?.[1] || "/screenshots/old_books.jpg"; + const image = indexEvent?.tags.find((tag) => tag[0] === "image")?.[1] || "/screenshots/old_books.jpg"; const currentUrl = `${url.origin}${url.pathname}`; return { - indexEvent, + indexEvent, // Will be null, triggering client-side fetch metadata: { title, summary, diff --git a/src/routes/publication/[type]/[identifier]/+layout.svelte b/src/routes/publication/[type]/[identifier]/+layout.svelte index a3b7be6..c14d288 100644 --- a/src/routes/publication/[type]/[identifier]/+layout.svelte +++ b/src/routes/publication/[type]/[identifier]/+layout.svelte @@ -3,6 +3,8 @@ import type { LayoutProps } from "./$types"; let { data, children }: LayoutProps = $props(); + + // AI-NOTE: Use metadata from server-side load for SEO and social sharing const { metadata } = data; diff --git a/src/routes/publication/[type]/[identifier]/+page.svelte b/src/routes/publication/[type]/[identifier]/+page.svelte index 11fd1f8..4452a48 100644 --- a/src/routes/publication/[type]/[identifier]/+page.svelte +++ b/src/routes/publication/[type]/[identifier]/+page.svelte @@ -9,12 +9,20 @@ import { page } from "$app/state"; import { goto } from "$app/navigation"; import { createNDKEvent } from "$lib/utils/nostrUtils"; + import { createTagAddress } from "$lib/utils"; + import { get } from "svelte/store"; + import { activeInboxRelays } from "$lib/ndk"; let { data }: PageProps = $props(); // data.indexEvent can be null from server-side rendering // We need to handle this case properly - const indexEvent = data.indexEvent ? createNDKEvent(data.ndk, data.indexEvent) : null; + // AI-NOTE: Always create NDK event since we now ensure NDK is available + const indexEvent = data.indexEvent && data.ndk + ? createNDKEvent(data.ndk, data.indexEvent) + : null; // No event if no NDK or no event data + + // Only create publication tree if we have a valid index event const publicationTree = indexEvent ? new SveltePublicationTree(indexEvent, data.ndk) : null; diff --git a/src/routes/publication/[type]/[identifier]/+page.ts b/src/routes/publication/[type]/[identifier]/+page.ts index 51c7d55..b2aefd4 100644 --- a/src/routes/publication/[type]/[identifier]/+page.ts +++ b/src/routes/publication/[type]/[identifier]/+page.ts @@ -3,29 +3,46 @@ import type { PageLoad } from "./$types"; import { fetchEventByDTag, fetchEventById, fetchEventByNaddr, fetchEventByNevent } from "../../../../lib/utils/websocket_utils.ts"; import type { NostrEvent } from "../../../../lib/utils/websocket_utils.ts"; -export const load: PageLoad = async ({ params }: { params: { type: string; identifier: string } }) => { +export const load: PageLoad = async ({ params, parent }: { params: { type: string; identifier: string }; parent: any }) => { + console.debug(`[Publication Load] Page load function called with params:`, params); const { type, identifier } = params; + console.debug(`[Publication Load] About to call parent()...`); + + // Get layout data (no server-side data since SSR is disabled) + const layoutData = await parent(); + console.debug(`[Publication Load] Layout data received:`, layoutData ? 'success' : 'null'); - let indexEvent: NostrEvent | null; + // AI-NOTE: Always fetch client-side since server-side fetch returns null for now + let indexEvent: NostrEvent | null = null; + console.debug(`[Publication Load] Fetching client-side for: ${identifier}`); + + try { + // Handle different identifier types + switch (type) { + case 'id': + indexEvent = await fetchEventById(identifier); + break; + case 'd': + indexEvent = await fetchEventByDTag(identifier); + break; + case 'naddr': + console.debug(`[Publication Load] Calling fetchEventByNaddr for: ${identifier}`); + indexEvent = await fetchEventByNaddr(identifier); + console.debug(`[Publication Load] fetchEventByNaddr returned:`, indexEvent ? 'success' : 'null'); + break; + case 'nevent': + indexEvent = await fetchEventByNevent(identifier); + break; + default: + error(400, `Unsupported identifier type: ${type}`); + } - // Handle different identifier types - switch (type) { - case 'id': - indexEvent = await fetchEventById(identifier); - break; - case 'd': - indexEvent = await fetchEventByDTag(identifier); - break; - case 'naddr': - indexEvent = await fetchEventByNaddr(identifier); - break; - case 'nevent': - indexEvent = await fetchEventByNevent(identifier); - break; - default: - error(400, `Unsupported identifier type: ${type}`); + console.debug(`[Publication Load] Client-side indexEvent after fetch:`, indexEvent ? 'success' : 'null'); + } catch (err) { + console.error(`[Publication Load] Error fetching event client-side:`, err); + throw err; } - + if (!indexEvent) { // AI-NOTE: Handle case where no relays are available during preloading // This prevents 404 errors when relay stores haven't been populated yet @@ -51,10 +68,35 @@ export const load: PageLoad = async ({ params }: { params: { type: string; ident error(404, `Event not found for ${type}: ${identifier}. href="/events?${searchParam}"`); } + console.debug(`[Publication Load] indexEvent details:`, { + id: indexEvent.id, + kind: indexEvent.kind, + pubkey: indexEvent.pubkey, + tags: indexEvent.tags.length, + contentLength: indexEvent.content.length + }); + const publicationType = indexEvent.tags.find((tag) => tag[0] === "type")?.[1] ?? ""; + + console.debug(`[Publication Load] publicationType:`, publicationType); + + // AI-NOTE: Use proper NDK instance from layout or create one with relays + let ndk = layoutData?.ndk; + if (!ndk) { + console.debug(`[Publication Load] Layout NDK not available, creating NDK instance with relays`); + // Import NDK dynamically to avoid SSR issues + const NDK = (await import("@nostr-dev-kit/ndk")).default; + // Import initNdk to get properly configured NDK with relays + const { initNdk } = await import("$lib/ndk"); + ndk = initNdk(); + } - return { + const result = { publicationType, indexEvent, + ndk, // Use minimal NDK instance }; + + console.debug(`[Publication Load] Returning result:`, result); + return result; };