Browse Source

fixed the publication loading

master
silberengel 7 months ago
parent
commit
12cf16b36d
  1. 21
      src/lib/utils.ts
  2. 36
      src/lib/utils/websocket_utils.ts
  3. 39
      src/routes/publication/[type]/[identifier]/+layout.server.ts
  4. 2
      src/routes/publication/[type]/[identifier]/+layout.svelte
  5. 10
      src/routes/publication/[type]/[identifier]/+page.svelte
  6. 82
      src/routes/publication/[type]/[identifier]/+page.ts

21
src/lib/utils.ts

@ -2,6 +2,7 @@ import type { NDKEvent } from "@nostr-dev-kit/ndk";
import { nip19 } from "nostr-tools"; import { nip19 } from "nostr-tools";
import { getMatchingTags } from "./utils/nostrUtils.ts"; import { getMatchingTags } from "./utils/nostrUtils.ts";
import type { AddressPointer, EventPointer } from "nostr-tools/nip19"; import type { AddressPointer, EventPointer } from "nostr-tools/nip19";
import type { NostrEvent } from "./utils/websocket_utils.ts";
export class DecodeError extends Error { export class DecodeError extends Error {
constructor(message: string) { 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[]) { export function nprofileEncode(pubkey: string, relays: string[]) {
return nip19.nprofileEncode({ pubkey, relays }); return nip19.nprofileEncode({ pubkey, relays });
} }

36
src/lib/utils/websocket_utils.ts

@ -152,26 +152,25 @@ export async function fetchNostrEvent(filter: NostrFilter): Promise<NostrEvent |
} }
}); });
// Wait for the first successful result with timeout // Wait for all relay results and find the first successful one
const timeoutPromise = new Promise<null>((resolve) => { const results = await Promise.allSettled(relayPromises);
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
]);
if (result) { // Find the first successful result
return 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; return null;
} }
@ -222,12 +221,15 @@ export async function fetchEventByNaddr(naddr: string): Promise<NostrEvent> {
authors: [decoded.pubkey], authors: [decoded.pubkey],
"#d": [decoded.identifier], "#d": [decoded.identifier],
}; };
console.debug(`[fetchEventByNaddr] Calling fetchNostrEvent with filter:`, filter);
const event = await fetchNostrEvent(filter); const event = await fetchNostrEvent(filter);
console.debug(`[fetchEventByNaddr] fetchNostrEvent returned:`, event ? 'success' : 'null');
if (!event) { if (!event) {
error(404, `Event not found for naddr: ${naddr}. href="/events?id=${naddr}"`); error(404, `Event not found for naddr: ${naddr}. href="/events?id=${naddr}"`);
} }
return event; return event;
} catch (err) { } catch (err) {
console.error(`[fetchEventByNaddr] Error:`, err);
if (err && typeof err === "object" && "status" in err) { if (err && typeof err === "object" && "status" in err) {
throw err; throw err;
} }

39
src/routes/publication/[type]/[identifier]/+layout.server.ts

@ -1,40 +1,29 @@
import { error } from "@sveltejs/kit"; import { error } from "@sveltejs/kit";
import type { LayoutServerLoad } from "./$types"; 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"; 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<NostrEvent | null> {
// 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 }) => { export const load: LayoutServerLoad = async ({ params, url }) => {
const { type, identifier } = params; const { type, identifier } = params;
let indexEvent: NostrEvent; // Try to fetch event server-side for metadata
const indexEvent = await fetchEventServerSide(type, identifier);
// 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}`);
}
// Extract metadata for meta tags // Extract metadata for meta tags (use fallbacks if no event found)
const title = indexEvent.tags.find((tag) => tag[0] === "title")?.[1] || "Alexandria Publication"; const title = indexEvent?.tags.find((tag) => tag[0] === "title")?.[1] || "Alexandria Publication";
const summary = indexEvent.tags.find((tag) => tag[0] === "summary")?.[1] || const summary = indexEvent?.tags.find((tag) => tag[0] === "summary")?.[1] ||
"Alexandria is a digital library, utilizing Nostr events for curated publications and wiki pages."; "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}`; const currentUrl = `${url.origin}${url.pathname}`;
return { return {
indexEvent, indexEvent, // Will be null, triggering client-side fetch
metadata: { metadata: {
title, title,
summary, summary,

2
src/routes/publication/[type]/[identifier]/+layout.svelte

@ -3,6 +3,8 @@
import type { LayoutProps } from "./$types"; import type { LayoutProps } from "./$types";
let { data, children }: LayoutProps = $props(); let { data, children }: LayoutProps = $props();
// AI-NOTE: Use metadata from server-side load for SEO and social sharing
const { metadata } = data; const { metadata } = data;
</script> </script>

10
src/routes/publication/[type]/[identifier]/+page.svelte

@ -9,12 +9,20 @@
import { page } from "$app/state"; import { page } from "$app/state";
import { goto } from "$app/navigation"; import { goto } from "$app/navigation";
import { createNDKEvent } from "$lib/utils/nostrUtils"; 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(); let { data }: PageProps = $props();
// data.indexEvent can be null from server-side rendering // data.indexEvent can be null from server-side rendering
// We need to handle this case properly // 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 // Only create publication tree if we have a valid index event
const publicationTree = indexEvent ? new SveltePublicationTree(indexEvent, data.ndk) : null; const publicationTree = indexEvent ? new SveltePublicationTree(indexEvent, data.ndk) : null;

82
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 { fetchEventByDTag, fetchEventById, fetchEventByNaddr, fetchEventByNevent } from "../../../../lib/utils/websocket_utils.ts";
import type { NostrEvent } 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; 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 console.debug(`[Publication Load] Client-side indexEvent after fetch:`, indexEvent ? 'success' : 'null');
switch (type) { } catch (err) {
case 'id': console.error(`[Publication Load] Error fetching event client-side:`, err);
indexEvent = await fetchEventById(identifier); throw err;
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}`);
} }
if (!indexEvent) { if (!indexEvent) {
// AI-NOTE: Handle case where no relays are available during preloading // AI-NOTE: Handle case where no relays are available during preloading
// This prevents 404 errors when relay stores haven't been populated yet // 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}"`); 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] ?? ""; 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, publicationType,
indexEvent, indexEvent,
ndk, // Use minimal NDK instance
}; };
console.debug(`[Publication Load] Returning result:`, result);
return result;
}; };

Loading…
Cancel
Save