Browse Source

Improve NDK state sharing

- Use SvelteKit context instead of stores to share NDK instance (see https://svelte.dev/docs/svelte/context)
- Clean up some dead code
- Move some standalone CSS into `app.css`
master
buttercat1791 7 months ago
parent
commit
0dab77b9dd
  1. 5
      src/app.css
  2. 16
      src/lib/components/CommentViewer.svelte
  3. 9
      src/lib/components/EventInput.svelte
  4. 13
      src/lib/components/EventSearch.svelte
  5. 5
      src/lib/components/LoginModal.svelte
  6. 14
      src/lib/components/Notifications.svelte
  7. 22
      src/lib/components/RelayActions.svelte
  8. 9
      src/lib/components/RelayDisplay.svelte
  9. 6
      src/lib/components/RelayStatus.svelte
  10. 6
      src/lib/components/embedded_events/EmbeddedEvent.svelte
  11. 14
      src/lib/components/embedded_events/EmbeddedSnippets.svelte
  12. 7
      src/lib/components/publications/PublicationFeed.svelte
  13. 7
      src/lib/components/util/Interactions.svelte
  14. 12
      src/lib/components/util/Profile.svelte
  15. 27
      src/lib/ndk.ts
  16. 10
      src/lib/services/publisher.ts
  17. 13
      src/lib/stores/userStore.ts
  18. 29
      src/lib/utils/event_input_utils.ts
  19. 18
      src/lib/utils/event_search.ts
  20. 26
      src/lib/utils/kind24_utils.ts
  21. 8
      src/lib/utils/nostrEventService.ts
  22. 10
      src/lib/utils/nostrUtils.ts
  23. 14
      src/lib/utils/profileCache.ts
  24. 15
      src/lib/utils/profile_search.ts
  25. 28
      src/lib/utils/subscription_search.ts
  26. 11
      src/lib/utils/tag_event_fetch.ts
  27. 29
      src/routes/+layout.svelte
  28. 8
      src/routes/+layout.ts
  29. 7
      src/routes/contact/+page.svelte
  30. 6
      src/routes/my-notes/+page.svelte
  31. 10
      src/routes/new/edit/+page.svelte
  32. 2
      src/routes/publication/+page.server.ts
  33. 29
      src/routes/publication/[type]/[identifier]/+layout.server.ts
  34. 17
      src/routes/publication/[type]/[identifier]/+page.ts
  35. 21
      src/routes/visualize/+page.svelte
  36. 5
      src/styles/events.css
  37. 9
      tests/unit/eventInput30040.test.ts
  38. 38
      tests/unit/tagExpansion.test.ts

5
src/app.css

@ -2,7 +2,6 @@ @@ -2,7 +2,6 @@
@import "./styles/scrollbar.css";
@import "./styles/publications.css";
@import "./styles/visualize.css";
@import "./styles/events.css";
@import "./styles/asciidoc.css";
/* Custom styles */
@ -321,6 +320,10 @@ @@ -321,6 +320,10 @@
}
@layer components {
canvas.qr-code {
@apply block mx-auto my-4;
}
/* Legend */
.leather-legend {
@apply relative m-4 sm:m-0 sm:absolute sm:top-1 sm:left-1 flex-shrink-0 p-2

16
src/lib/components/CommentViewer.svelte

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
import { Button, P, Heading } from "flowbite-svelte";
import { getUserMetadata, toNpub } from "$lib/utils/nostrUtils";
import { neventEncode } from "$lib/utils";
import { activeInboxRelays, ndkInstance } from "$lib/ndk";
import { activeInboxRelays, getNdkContext } from "$lib/ndk";
import { goto } from "$app/navigation";
import { onMount } from "svelte";
import type { NDKEvent } from "@nostr-dev-kit/ndk";
@ -10,6 +10,8 @@ @@ -10,6 +10,8 @@
const { event } = $props<{ event: NDKEvent }>();
const ndk = getNdkContext();
// AI-NOTE: 2025-01-08 - Clean, efficient comment viewer implementation
// This component fetches and displays threaded comments with proper hierarchy
// Uses simple, reliable profile fetching and efficient state management
@ -124,15 +126,15 @@ @@ -124,15 +126,15 @@
// Get all available relays for a more comprehensive search
// Use the full NDK pool relays instead of just active relays
const ndkPoolRelays = Array.from($ndkInstance.pool.relays.values()).map(relay => relay.url);
const ndkPoolRelays = Array.from(ndk.pool.relays.values()).map(relay => relay.url);
console.log(`[CommentViewer] Using ${ndkPoolRelays.length} NDK pool relays for search:`, ndkPoolRelays);
// Try all filters to find comments with full relay set
activeSub = $ndkInstance.subscribe(filters);
activeSub = ndk.subscribe(filters);
// Also try a direct search for the specific comment we're looking for
console.log(`[CommentViewer] Also searching for specific comment: 64173a81c2a8e26342d4a75d3def804da8644377bde99cfdfeaf189dff87f942`);
const specificCommentSub = $ndkInstance.subscribe({
const specificCommentSub = ndk.subscribe({
ids: ["64173a81c2a8e26342d4a75d3def804da8644377bde99cfdfeaf189dff87f942"]
});
@ -291,7 +293,7 @@ @@ -291,7 +293,7 @@
try {
// Try a broader search to see if there are any events that might be comments
const testSub = $ndkInstance.subscribe({
const testSub = ndk.subscribe({
kinds: [1, 1111, 9802],
"#e": [event.id],
limit: 10,
@ -462,7 +464,7 @@ @@ -462,7 +464,7 @@
console.log(`[CommentViewer] Fetching nested replies for event:`, eventId);
// Search for replies to this specific event
const nestedSub = $ndkInstance.subscribe({
const nestedSub = ndk.subscribe({
kinds: [1, 1111, 9802],
"#e": [eventId],
limit: 50,
@ -506,7 +508,7 @@ @@ -506,7 +508,7 @@
if (dTag) {
const eventAddress = `${event.kind}:${event.pubkey}:${dTag}`;
const nip22Sub = $ndkInstance.subscribe({
const nip22Sub = ndk.subscribe({
kinds: [1111, 9802],
"#a": [eventAddress],
limit: 50,

9
src/lib/components/EventInput.svelte

@ -13,24 +13,24 @@ @@ -13,24 +13,24 @@
get30040FixGuidance,
} from "$lib/utils/event_input_utils";
import {
extractDocumentMetadata,
extractSmartMetadata,
metadataToTags,
removeMetadataFromContent
} from "$lib/utils/asciidoc_metadata";
import { get } from "svelte/store";
import { ndkInstance } from "$lib/ndk";
import { userPubkey } from "$lib/stores/authStore.Svelte";
import { userStore } from "$lib/stores/userStore";
import { NDKEvent as NDKEventClass } from "@nostr-dev-kit/ndk";
import NDK, { NDKEvent as NDKEventClass } from "@nostr-dev-kit/ndk";
import type { NDKEvent } from "$lib/utils/nostrUtils";
import { prefixNostrAddresses } from "$lib/utils/nostrUtils";
import { activeInboxRelays, activeOutboxRelays } from "$lib/ndk";
import { activeInboxRelays, activeOutboxRelays, getNdkContext } from "$lib/ndk";
import { Button } from "flowbite-svelte";
import { goto } from "$app/navigation";
import { WebSocketPool } from "$lib/data_structures/websocket_pool";
import { anonymousRelays } from "$lib/consts";
const ndk = getNdkContext();
let kind = $state<number>(30040);
let tags = $state<[string, string][]>([]);
let content = $state("");
@ -221,7 +221,6 @@ @@ -221,7 +221,6 @@
createdAt = Math.floor(Date.now() / 1000);
try {
const ndk = get(ndkInstance);
const currentUserPubkey = get(userPubkey as any);
const userState = get(userStore);

13
src/lib/components/EventSearch.svelte

@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
<script lang="ts">
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
import { Input, Button } from "flowbite-svelte";
import { Spinner } from "flowbite-svelte";
@ -10,12 +9,10 @@ @@ -10,12 +9,10 @@
searchNip05,
} from "$lib/utils/search_utility";
import { neventEncode, naddrEncode, nprofileEncode } from "$lib/utils";
import { activeInboxRelays, activeOutboxRelays, ndkInstance } from "$lib/ndk";
import { searchRelays } from "$lib/consts";
import { activeInboxRelays, activeOutboxRelays, getNdkContext } from "$lib/ndk";
import { getMatchingTags, toNpub } from "$lib/utils/nostrUtils";
import type { SearchResult } from '$lib/utils/search_types';
import { userStore } from "$lib/stores/userStore";
import { get } from "svelte/store";
import type NDK from '@nostr-dev-kit/ndk';
// Props definition
let {
loading,
@ -47,6 +44,8 @@ @@ -47,6 +44,8 @@
onLoadingChange?: (loading: boolean) => void;
} = $props();
const ndk = getNdkContext();
// Component state
let searchQuery = $state("");
let localError = $state<string | null>(null);
@ -509,7 +508,6 @@ @@ -509,7 +508,6 @@
// This ensures searches can proceed even if some relay types are not available
while (retryCount < maxRetries) {
// Check if we have any relays in the NDK pool
const ndk = get(ndkInstance);
if (ndk && ndk.pool && ndk.pool.relays && ndk.pool.relays.size > 0) {
console.debug(`EventSearch: Found ${ndk.pool.relays.size} relays in NDK pool`);
break;
@ -528,7 +526,6 @@ @@ -528,7 +526,6 @@
// AI-NOTE: 2025-01-24 - Don't fail if no relays are available, let the search functions handle fallbacks
// The search functions will use all available relays including fallback relays
const ndk = get(ndkInstance);
const poolRelayCount = ndk?.pool?.relays?.size || 0;
console.log("EventSearch: Relay status for search:", {

5
src/lib/components/LoginModal.svelte

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
import { Button, Modal } from "flowbite-svelte";
import { loginWithExtension } from "$lib/stores/userStore";
import { userStore } from "$lib/stores/userStore";
import { getNdkContext } from "$lib/ndk";
const {
show = false,
@ -13,6 +14,8 @@ @@ -13,6 +14,8 @@
onLoginSuccess?: () => void;
}>();
const ndk = getNdkContext();
let signInFailed = $state<boolean>(false);
let errorMessage = $state<string>("");
let user = $state($userStore);
@ -42,7 +45,7 @@ @@ -42,7 +45,7 @@
signInFailed = false;
errorMessage = "";
await loginWithExtension();
await loginWithExtension(ndk);
} catch (e: unknown) {
console.error(e);
signInFailed = true;

14
src/lib/components/Notifications.svelte

@ -3,7 +3,6 @@ @@ -3,7 +3,6 @@
import { Heading, P } from "flowbite-svelte";
import type { NDKEvent } from "$lib/utils/nostrUtils";
import { userStore } from "$lib/stores/userStore";
import { ndkInstance } from "$lib/ndk";
import { goto } from "$app/navigation";
import { get } from "svelte/store";
import { nip19 } from "nostr-tools";
@ -23,9 +22,12 @@ @@ -23,9 +22,12 @@
import { formatDate, neventEncode } from "$lib/utils";
import { NDKRelaySetFromNDK } from "$lib/utils/nostrUtils";
import EmbeddedEvent from "./embedded_events/EmbeddedEvent.svelte";
import { getNdkContext } from "$lib/ndk";
const { event } = $props<{ event: NDKEvent }>();
const ndk = getNdkContext();
// Handle navigation events from quoted messages
$effect(() => {
if (typeof window !== 'undefined') {
@ -465,7 +467,6 @@ @@ -465,7 +467,6 @@
error = null;
try {
const ndk = get(ndkInstance);
if (!ndk) throw new Error("No NDK instance available");
const userStoreValue = get(userStore);
@ -502,7 +503,7 @@ @@ -502,7 +503,7 @@
.sort((a, b) => (b.created_at || 0) - (a.created_at || 0))
.slice(0, 100);
authorProfiles = await fetchAuthorProfiles(notifications);
authorProfiles = await fetchAuthorProfiles(notifications, ndk);
} catch (err) {
console.error("[Notifications] Error fetching notifications:", err);
error = err instanceof Error ? err.message : "Failed to fetch notifications";
@ -519,7 +520,6 @@ @@ -519,7 +520,6 @@
error = null;
try {
const ndk = get(ndkInstance);
if (!ndk) throw new Error("No NDK instance available");
const userStoreValue = get(userStore);
@ -550,7 +550,7 @@ @@ -550,7 +550,7 @@
.sort((a, b) => (b.created_at || 0) - (a.created_at || 0))
.slice(0, 200);
authorProfiles = await fetchAuthorProfiles(publicMessages);
authorProfiles = await fetchAuthorProfiles(publicMessages, ndk);
} catch (err) {
console.error("[PublicMessages] Error fetching public messages:", err);
error = err instanceof Error ? err.message : "Failed to fetch public messages";
@ -637,7 +637,6 @@ @@ -637,7 +637,6 @@
// If no relays found from NIP-65, use fallback relays
if (uniqueRelays.length === 0) {
console.log("[Relay Effect] No NIP-65 relays found, using fallback");
const ndk = get(ndkInstance);
if (ndk) {
const userStoreValue = get(userStore);
const user = userStoreValue.signedIn && userStoreValue.pubkey ? ndk.getUser({ pubkey: userStoreValue.pubkey }) : null;
@ -653,7 +652,6 @@ @@ -653,7 +652,6 @@
} catch (error) {
console.error("[Relay Effect] Error getting relay set:", error);
console.log("[Relay Effect] Using fallback relays due to error");
const ndk = get(ndkInstance);
if (ndk) {
const userStoreValue = get(userStore);
const user = userStoreValue.signedIn && userStoreValue.pubkey ? ndk.getUser({ pubkey: userStoreValue.pubkey }) : null;
@ -813,7 +811,7 @@ @@ -813,7 +811,7 @@
{#if message.getMatchingTags("q").length > 0}
<div class="text-sm text-gray-800 dark:text-gray-200 mb-2 leading-relaxed">
{@render quotedContent(message, publicMessages)}
{@render quotedContent(message, publicMessages, ndk)}
</div>
{/if}
{#if message.content}

22
src/lib/components/RelayActions.svelte

@ -1,35 +1,26 @@ @@ -1,35 +1,26 @@
<script lang="ts">
import { Button, Modal } from "flowbite-svelte";
import { ndkInstance, activeInboxRelays, activeOutboxRelays } from "$lib/ndk";
import { get } from "svelte/store";
import { Modal } from "flowbite-svelte";
import { activeInboxRelays, activeOutboxRelays, getNdkContext } from "$lib/ndk";
import type { NDKEvent } from "$lib/utils/nostrUtils";
import {
createRelaySetFromUrls,
createNDKEvent,
} from "$lib/utils/nostrUtils";
import RelayDisplay, {
getConnectedRelays,
getEventRelays,
} from "./RelayDisplay.svelte";
import { communityRelays, secondaryRelays } from "$lib/consts";
const { event } = $props<{
event: NDKEvent;
}>();
let searchingRelays = $state(false);
let foundRelays = $state<string[]>([]);
const ndk = getNdkContext();
let showRelayModal = $state(false);
let relaySearchResults = $state<
Record<string, "pending" | "found" | "notfound">
>({});
let allRelays = $state<string[]>([]);
// Magnifying glass icon SVG
const searchIcon = `<svg class="w-4 h-4 mr-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"/>
</svg>`;
function openRelayModal() {
showRelayModal = true;
relaySearchResults = {};
@ -39,7 +30,6 @@ @@ -39,7 +30,6 @@
async function searchAllRelaysLive() {
if (!event) return;
relaySearchResults = {};
const ndk = get(ndkInstance);
const userRelays = Array.from(ndk?.pool?.relays.values() || []).map(
(r) => r.url,
);
@ -66,10 +56,6 @@ @@ -66,10 +56,6 @@
}),
);
}
function closeRelayModal() {
showRelayModal = false;
}
</script>
<div class="mt-2">

9
src/lib/components/RelayDisplay.svelte

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
<script lang="ts" context="module">
import type { NDKEvent } from "$lib/utils/nostrUtils";
import { get } from "svelte/store";
import { activeInboxRelays, ndkInstance } from "$lib/ndk";
import { activeInboxRelays } from "$lib/ndk";
// Get relays from event (prefer event.relay or event.relays, fallback to active inbox relays)
export function getEventRelays(event: NDKEvent): string[] {
@ -17,13 +17,6 @@ @@ -17,13 +17,6 @@
// Use active inbox relays as fallback
return get(activeInboxRelays);
}
export function getConnectedRelays(): string[] {
const ndk = get(ndkInstance);
return Array.from(ndk?.pool?.relays.values() || [])
.filter((r) => r.status === 1) // Only use connected relays
.map((r) => r.url);
}
</script>
<script lang="ts">

6
src/lib/components/RelayStatus.svelte

@ -1,14 +1,15 @@ @@ -1,14 +1,15 @@
<script lang="ts">
import { Button, Alert } from "flowbite-svelte";
import {
ndkInstance,
ndkSignedIn,
testRelayConnection,
checkWebSocketSupport,
checkEnvironmentForWebSocketDowngrade,
} from "$lib/ndk";
import { onMount } from "svelte";
import { activeInboxRelays, activeOutboxRelays } from "$lib/ndk";
import { activeInboxRelays, activeOutboxRelays, getNdkContext } from "$lib/ndk";
const ndk = getNdkContext();
interface RelayStatus {
url: string;
@ -30,7 +31,6 @@ import { activeInboxRelays, activeOutboxRelays } from "$lib/ndk"; @@ -30,7 +31,6 @@ import { activeInboxRelays, activeOutboxRelays } from "$lib/ndk";
async function runRelayTests() {
testing = true;
const ndk = $ndkInstance;
if (!ndk) {
testing = false;
return;

6
src/lib/components/embedded_events/EmbeddedEvent.svelte

@ -5,11 +5,10 @@ @@ -5,11 +5,10 @@
import { userBadge } from "$lib/snippets/UserSnippets.svelte";
import { parsedContent } from "$lib/components/embedded_events/EmbeddedSnippets.svelte";
import { naddrEncode } from "$lib/utils";
import { activeInboxRelays, ndkInstance } from "$lib/ndk";
import { activeInboxRelays, getNdkContext } from "$lib/ndk";
import { goto } from "$app/navigation";
import { getEventType } from "$lib/utils/mime";
import { nip19 } from "nostr-tools";
import { get } from "svelte/store";
import { repostKinds } from "$lib/consts";
const {
@ -20,6 +19,8 @@ @@ -20,6 +19,8 @@
nestingLevel?: number;
}>();
const ndk = getNdkContext();
let event = $state<NDKEvent | null>(null);
let profile = $state<{
name?: string;
@ -58,7 +59,6 @@ @@ -58,7 +59,6 @@
error = null;
try {
const ndk = get(ndkInstance);
if (!ndk) {
throw new Error("No NDK instance available");
}

14
src/lib/components/embedded_events/EmbeddedSnippets.svelte

@ -2,13 +2,12 @@ @@ -2,13 +2,12 @@
import type { NDKEvent } from "$lib/utils/nostrUtils";
import { NDKRelaySetFromNDK, toNpub, getUserMetadata } from "$lib/utils/nostrUtils";
import { get } from "svelte/store";
import { ndkInstance } from "$lib/ndk";
import { searchRelays } from "$lib/consts";
import { userStore, type UserState } from "$lib/stores/userStore";
import { buildCompleteRelaySet } from "$lib/utils/relay_management";
import { nip19 } from "nostr-tools";
import type NDK from "@nostr-dev-kit/ndk";
import { parseEmbeddedMarkup } from "$lib/utils/markup/embeddedMarkupParser";
import type NDK from "@nostr-dev-kit/ndk";
export {
parsedContent,
@ -91,7 +90,7 @@ @@ -91,7 +90,7 @@
/**
* Fetches author profiles for a list of events
*/
async function fetchAuthorProfiles(events: NDKEvent[]): Promise<Map<string, { name?: string; displayName?: string; picture?: string }>> {
async function fetchAuthorProfiles(events: NDKEvent[], ndk: NDK): Promise<Map<string, { name?: string; displayName?: string; picture?: string }>> {
const authorProfiles = new Map<string, { name?: string; displayName?: string; picture?: string }>();
const uniquePubkeys = new Set<string>();
@ -114,7 +113,6 @@ @@ -114,7 +113,6 @@
// Try search relays
for (const relay of searchRelays) {
try {
const ndk: NDK | undefined = get(ndkInstance);
if (!ndk) break;
const relaySet = NDKRelaySetFromNDK.fromRelayUrls([relay], ndk);
@ -140,7 +138,6 @@ @@ -140,7 +138,6 @@
// Try all available relays as fallback
try {
const ndk: NDK | undefined = get(ndkInstance);
if (!ndk) return;
const userStoreValue: UserState = get(userStore);
@ -177,7 +174,7 @@ @@ -177,7 +174,7 @@
return authorProfiles;
}
async function findQuotedMessage(eventId: string, publicMessages: NDKEvent[]): Promise<NDKEvent | undefined> {
async function findQuotedMessage(eventId: string, publicMessages: NDKEvent[], ndk: NDK): Promise<NDKEvent | undefined> {
// Validate eventId format (should be 64 character hex string)
const isValidEventId = /^[a-fA-F0-9]{64}$/.test(eventId);
if (!isValidEventId) return undefined;
@ -188,7 +185,6 @@ @@ -188,7 +185,6 @@
// If not found locally, fetch from relays
if (!quotedMessage) {
try {
const ndk: NDK | undefined = get(ndkInstance);
if (ndk) {
const userStoreValue: UserState = get(userStore);
const user = userStoreValue.signedIn && userStoreValue.pubkey ? ndk.getUser({ pubkey: userStoreValue.pubkey }) : null;
@ -274,14 +270,14 @@ @@ -274,14 +270,14 @@
{/if}
{/snippet}
{#snippet quotedContent(message: NDKEvent, publicMessages: NDKEvent[])}
{#snippet quotedContent(message: NDKEvent, publicMessages: NDKEvent[], ndk: NDK)}
{@const qTags = message.getMatchingTags("q")}
{#if qTags.length > 0}
{@const qTag = qTags[0]}
{@const eventId = qTag[1]}
{#if eventId}
{#await findQuotedMessage(eventId, publicMessages) then quotedMessage}
{#await findQuotedMessage(eventId, publicMessages, ndk) then quotedMessage}
{#if quotedMessage}
{@const quotedContent = quotedMessage.content ? quotedMessage.content.slice(0, 200) : "No content"}
{#await parseEmbeddedMarkup(quotedContent, 0) then parsedContent}

7
src/lib/components/publications/PublicationFeed.svelte

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
<script lang="ts">
import { indexKind } from "$lib/consts";
import { ndkInstance, activeInboxRelays, activeOutboxRelays } from "$lib/ndk";
import { activeInboxRelays, activeOutboxRelays, getNdkContext } from "$lib/ndk";
import { filterValidIndexEvents, debounceAsync } from "$lib/utils";
import { Button, P, Skeleton, Spinner } from "flowbite-svelte";
import ArticleHeader from "./PublicationHeader.svelte";
@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
toNpub,
} from "$lib/utils/nostrUtils";
import { WebSocketPool } from "$lib/data_structures/websocket_pool";
import { NDKEvent } from "@nostr-dev-kit/ndk";
import NDK, { NDKEvent } from "@nostr-dev-kit/ndk";
import { searchCache } from "$lib/utils/searchCache";
import { indexEventCache } from "$lib/utils/indexEventCache";
import { isValidNip05Address } from "$lib/utils/search_utility";
@ -23,6 +23,8 @@ @@ -23,6 +23,8 @@
onEventCountUpdate?: (counts: { displayed: number; total: number }) => void;
}>();
const ndk = getNdkContext();
// Component state
let eventsInView: NDKEvent[] = $state([]);
let loadingMore: boolean = $state(false);
@ -35,7 +37,6 @@ @@ -35,7 +37,6 @@
// Relay management
let allRelays: string[] = $state([]);
let ndk = $derived($ndkInstance);
// Event management
let allIndexEvents: NDKEvent[] = $state([]);

7
src/lib/components/util/Interactions.svelte

@ -8,15 +8,16 @@ @@ -8,15 +8,16 @@
import ZapOutline from "$components/util/ZapOutline.svelte";
import type { NDKEvent } from "@nostr-dev-kit/ndk";
import { onMount } from "svelte";
import { ndkInstance } from "$lib/ndk";
import { publicationColumnVisibility } from "$lib/stores";
import { getNdkContext } from "$lib/ndk";
const {
rootId,
event,
direction = "row",
} = $props<{ rootId: string; event?: NDKEvent; direction?: string }>();
const ndk = getNdkContext();
// Reactive arrays to hold incoming events
let likes: NDKEvent[] = [];
let zaps: NDKEvent[] = [];
@ -38,7 +39,7 @@ @@ -38,7 +39,7 @@
* Returns the subscription for later cleanup.
*/
function subscribeCount(kind: number, targetArray: NDKEvent[]) {
const sub = $ndkInstance.subscribe({
const sub = ndk.subscribe({
kinds: [kind],
"#a": [rootId], // Will this work?
});

12
src/lib/components/util/Profile.svelte

@ -8,22 +8,21 @@ @@ -8,22 +8,21 @@
loginWithAmber,
loginWithNpub
} from "$lib/stores/userStore";
import { ndkInstance } from "$lib/ndk";
import {
ArrowRightToBracketOutline,
UserOutline,
FileSearchOutline,
} from "flowbite-svelte-icons";
import { Avatar, Popover } from "flowbite-svelte";
import type { NDKUserProfile } from "@nostr-dev-kit/ndk";
import { get } from "svelte/store";
import { goto } from "$app/navigation";
import NDK, { NDKNip46Signer, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
import { onMount } from "svelte";
import { getUserMetadata } from "$lib/utils/nostrUtils";
import { activeInboxRelays, activeOutboxRelays } from "$lib/ndk";
import { activeInboxRelays, activeOutboxRelays, getNdkContext } from "$lib/ndk";
let { pubkey, isNav = false } = $props<{ pubkey?: string, isNav?: boolean }>();
const ndk = getNdkContext();
let { isNav = false } = $props<{ isNav?: boolean }>();
// UI state for login functionality
let isLoadingExtension: boolean = $state(false);
@ -205,7 +204,6 @@ @@ -205,7 +204,6 @@
}
// Try using NDK's built-in profile fetching first
const ndk = get(ndkInstance);
if (ndk && userState.ndkUser) {
console.log("Using NDK's built-in profile fetching");
const userProfile = await userState.ndkUser.fetchProfile();
@ -298,7 +296,7 @@ @@ -298,7 +296,7 @@
isLoadingExtension = true;
isLoadingAmber = false;
try {
await loginWithExtension();
await loginWithExtension(ndk);
} catch (err: unknown) {
showResultMessage(
`❌ Browser extension connection failed: ${err instanceof Error ? err.message : String(err)}`,

27
src/lib/ndk.ts

@ -13,9 +13,6 @@ import { @@ -13,9 +13,6 @@ import {
deduplicateRelayUrls,
testRelayConnection,
} from "./utils/relay_management.ts";
// Re-export testRelayConnection for components that need it
export { testRelayConnection };
import { userStore } from "./stores/userStore.ts";
import { userPubkey } from "./stores/authStore.Svelte.ts";
import {
@ -23,8 +20,11 @@ import { @@ -23,8 +20,11 @@ import {
stopNetworkStatusMonitoring,
} from "./stores/networkStore.ts";
import { WebSocketPool } from "./data_structures/websocket_pool.ts";
import { getContext, setContext } from "svelte";
// Re-export testRelayConnection for components that need it
export { testRelayConnection };
export const ndkInstance: Writable<NDK> = writable();
export const ndkSignedIn = writable(false);
export const activePubkey = writable<string | null>(null);
export const inboxRelays = writable<string[]>([]);
@ -34,6 +34,16 @@ export const outboxRelays = writable<string[]>([]); @@ -34,6 +34,16 @@ export const outboxRelays = writable<string[]>([]);
export const activeInboxRelays = writable<string[]>([]);
export const activeOutboxRelays = writable<string[]>([]);
const NDK_CONTEXT_KEY = "ndk";
export function getNdkContext(): NDK {
return getContext(NDK_CONTEXT_KEY) as NDK;
}
export function setNdkContext(ndk: NDK): void {
setContext(NDK_CONTEXT_KEY, ndk);
}
// AI-NOTE: 2025-01-08 - Persistent relay storage to avoid recalculation
let persistentRelaySet:
| { inboxRelays: string[]; outboxRelays: string[] }
@ -763,7 +773,6 @@ export function initNdk(): NDK { @@ -763,7 +773,6 @@ export function initNdk(): NDK {
ndkSignedIn.set(userState.signedIn);
// Refresh relay stores when user state changes
const ndk = get(ndkInstance);
if (ndk) {
try {
await refreshRelayStores(ndk);
@ -786,7 +795,7 @@ export function initNdk(): NDK { @@ -786,7 +795,7 @@ export function initNdk(): NDK {
export function cleanupNdk(): void {
console.debug("[NDK.ts] Cleaning up NDK resources");
const ndk = get(ndkInstance);
const ndk = getNdkContext();
if (ndk) {
try {
// Disconnect from all relays
@ -818,7 +827,7 @@ export async function loginWithExtension( @@ -818,7 +827,7 @@ export async function loginWithExtension(
pubkey?: string,
): Promise<NDKUser | null> {
try {
const ndk = get(ndkInstance);
const ndk = getNdkContext();
const signer = new NDKNip07Signer();
const signerUser = await signer.user();
@ -838,7 +847,7 @@ export async function loginWithExtension( @@ -838,7 +847,7 @@ export async function loginWithExtension(
ndk.signer = signer;
ndk.activeUser = user;
ndkInstance.set(ndk);
setNdkContext(ndk);
ndkSignedIn.set(true);
return user;
@ -872,5 +881,5 @@ export function logout(user: NDKUser): void { @@ -872,5 +881,5 @@ export function logout(user: NDKUser): void {
// Re-initialize with anonymous instance
const newNdk = initNdk();
ndkInstance.set(newNdk);
setNdkContext(newNdk);
}

10
src/lib/services/publisher.ts

@ -1,11 +1,9 @@ @@ -1,11 +1,9 @@
import { get } from "svelte/store";
import { ndkInstance } from "../ndk.ts";
import { getMimeTags } from "../utils/mime.ts";
import {
metadataToTags,
parseAsciiDocWithMetadata,
} from "../utils/asciidoc_metadata.ts";
import { NDKEvent, NDKRelaySet } from "@nostr-dev-kit/ndk";
import NDK, { NDKEvent, NDKRelaySet } from "@nostr-dev-kit/ndk";
import { nip19 } from "nostr-tools";
export interface PublishResult {
@ -28,6 +26,7 @@ export interface PublishOptions { @@ -28,6 +26,7 @@ export interface PublishOptions {
*/
export async function publishZettel(
options: PublishOptions,
ndk: NDK,
): Promise<PublishResult> {
const { content, kind = 30041, onSuccess, onError } = options;
@ -37,9 +36,6 @@ export async function publishZettel( @@ -37,9 +36,6 @@ export async function publishZettel(
return { success: false, error };
}
// Get the current NDK instance from the store
const ndk = get(ndkInstance);
if (!ndk?.activeUser) {
const error = "Please log in first";
onError?.(error);
@ -115,6 +111,7 @@ export async function publishZettel( @@ -115,6 +111,7 @@ export async function publishZettel(
*/
export async function publishMultipleZettels(
options: PublishOptions,
ndk: NDK,
): Promise<PublishResult[]> {
const { content, kind = 30041, onError } = options;
@ -124,7 +121,6 @@ export async function publishMultipleZettels( @@ -124,7 +121,6 @@ export async function publishMultipleZettels(
return [{ success: false, error }];
}
const ndk = get(ndkInstance);
if (!ndk?.activeUser) {
const error = "Please log in first";
onError?.(error);

13
src/lib/stores/userStore.ts

@ -11,7 +11,6 @@ import { getUserMetadata } from "../utils/nostrUtils.ts"; @@ -11,7 +11,6 @@ import { getUserMetadata } from "../utils/nostrUtils.ts";
import {
activeInboxRelays,
activeOutboxRelays,
ndkInstance,
updateActiveRelayStores,
} from "../ndk.ts";
import { loginStorageKey } from "../consts.ts";
@ -166,8 +165,7 @@ function clearLogin() { @@ -166,8 +165,7 @@ function clearLogin() {
/**
* Login with NIP-07 browser extension
*/
export async function loginWithExtension() {
const ndk = get(ndkInstance);
export async function loginWithExtension(ndk: NDK) {
if (!ndk) throw new Error("NDK not initialized");
// Only clear previous login state after successful login
const signer = new NDKNip07Signer();
@ -246,8 +244,7 @@ export async function loginWithExtension() { @@ -246,8 +244,7 @@ export async function loginWithExtension() {
/**
* Login with Amber (NIP-46)
*/
export async function loginWithAmber(amberSigner: NDKSigner, user: NDKUser) {
const ndk = get(ndkInstance);
export async function loginWithAmber(amberSigner: NDKSigner, user: NDKUser, ndk: NDK) {
if (!ndk) throw new Error("NDK not initialized");
// Only clear previous login state after successful login
const npub = user.npub;
@ -321,8 +318,7 @@ export async function loginWithAmber(amberSigner: NDKSigner, user: NDKUser) { @@ -321,8 +318,7 @@ export async function loginWithAmber(amberSigner: NDKSigner, user: NDKUser) {
/**
* Login with npub (read-only)
*/
export async function loginWithNpub(pubkeyOrNpub: string) {
const ndk = get(ndkInstance);
export async function loginWithNpub(pubkeyOrNpub: string, ndk: NDK) {
if (!ndk) {
throw new Error("NDK not initialized");
}
@ -413,7 +409,7 @@ export async function loginWithNpub(pubkeyOrNpub: string) { @@ -413,7 +409,7 @@ export async function loginWithNpub(pubkeyOrNpub: string) {
/**
* Logout and clear all user state
*/
export function logoutUser() {
export function logoutUser(ndk: NDK) {
console.log("Logging out user...");
const currentUser = get(userStore);
@ -476,7 +472,6 @@ export function logoutUser() { @@ -476,7 +472,6 @@ export function logoutUser() {
});
userPubkey.set(null);
const ndk = get(ndkInstance);
if (ndk) {
ndk.activeUser = undefined;
ndk.signer = undefined;

29
src/lib/utils/event_input_utils.ts

@ -1,15 +1,11 @@ @@ -1,15 +1,11 @@
import type { NDKEvent } from "./nostrUtils.ts";
import { get } from "svelte/store";
import { ndkInstance } from "../ndk.ts";
import { NDKEvent as NDKEventClass } from "@nostr-dev-kit/ndk";
import NDK, { NDKEvent as NDKEventClass } from "@nostr-dev-kit/ndk";
import { EVENT_KINDS } from "./search_constants";
import {
extractDocumentMetadata,
extractSectionMetadata,
metadataToTags,
parseAsciiDocWithMetadata,
removeMetadataFromContent,
} from "./asciidoc_metadata";
} from "./asciidoc_metadata.ts";
// =========================
// Validation
@ -151,7 +147,7 @@ export function validate30040EventSet(content: string): { @@ -151,7 +147,7 @@ export function validate30040EventSet(content: string): {
}
// Check for empty sections
const emptySections = parsed.sections.filter((section) =>
const emptySections = parsed.sections.filter((section: any) =>
section.content.trim() === ""
);
if (emptySections.length > 0) {
@ -200,12 +196,6 @@ function extractMarkdownTopHeader(content: string): string | null { @@ -200,12 +196,6 @@ function extractMarkdownTopHeader(content: string): string | null {
// Event Construction
// =========================
/**
* Returns the current NDK instance from the store.
*/
function getNdk() {
return get(ndkInstance);
}
/**
* Builds a set of events for a 30040 publication: one 30040 index event and one 30041 event per section.
@ -216,15 +206,8 @@ export function build30040EventSet( @@ -216,15 +206,8 @@ export function build30040EventSet(
content: string,
tags: [string, string][],
baseEvent: Partial<NDKEvent> & { pubkey: string; created_at: number },
ndk: NDK,
): { indexEvent: NDKEvent; sectionEvents: NDKEvent[] } {
console.log("=== build30040EventSet called ===");
console.log("Input content:", content);
console.log("Input tags:", tags);
console.log("Input baseEvent:", baseEvent);
const ndk = getNdk();
console.log("NDK instance:", ndk);
// Parse the AsciiDoc content with metadata extraction
const parsed = parseAsciiDocWithMetadata(content);
console.log("Parsed AsciiDoc:", parsed);
@ -262,8 +245,6 @@ export function build30040EventSet( @@ -262,8 +245,6 @@ export function build30040EventSet(
created_at: baseEvent.created_at,
});
console.log("Final index event (index card):", indexEvent);
console.log("=== build30040EventSet completed (index card) ===");
return { indexEvent, sectionEvents: [] };
}
@ -272,7 +253,7 @@ export function build30040EventSet( @@ -272,7 +253,7 @@ export function build30040EventSet(
console.log("Index event:", { documentTitle, indexDTag });
// Create section events with their metadata
const sectionEvents: NDKEvent[] = parsed.sections.map((section, i) => {
const sectionEvents: NDKEvent[] = parsed.sections.map((section: any, i: number) => {
const sectionDTag = `${indexDTag}-${normalizeDTagValue(section.title)}`;
console.log(`Creating section ${i}:`, {
title: section.title,

18
src/lib/utils/event_search.ts

@ -1,7 +1,6 @@ @@ -1,7 +1,6 @@
import { ndkInstance } from "../ndk.ts";
import { fetchEventWithFallback, NDKRelaySetFromNDK } from "./nostrUtils.ts";
import { nip19 } from "nostr-tools";
import { NDKEvent } from "@nostr-dev-kit/ndk";
import NDK, { NDKEvent } from "@nostr-dev-kit/ndk";
import type { Filter } from "./search_types.ts";
import { get } from "svelte/store";
import { isValidNip05Address, wellKnownUrl } from "./search_utils.ts";
@ -11,8 +10,7 @@ import { activeInboxRelays, activeOutboxRelays } from "../ndk.ts"; @@ -11,8 +10,7 @@ import { activeInboxRelays, activeOutboxRelays } from "../ndk.ts";
/**
* Search for a single event by ID or filter
*/
export async function searchEvent(query: string): Promise<NDKEvent | null> {
const ndk = get(ndkInstance);
export async function searchEvent(query: string, ndk: NDK): Promise<NDKEvent | null> {
if (!ndk) {
console.warn("[Search] No NDK instance available");
return null;
@ -68,14 +66,14 @@ export async function searchEvent(query: string): Promise<NDKEvent | null> { @@ -68,14 +66,14 @@ export async function searchEvent(query: string): Promise<NDKEvent | null> {
// Try as event id
filterOrId = cleanedQuery;
const eventResult = await fetchEventWithFallback(
get(ndkInstance),
ndk,
filterOrId,
TIMEOUTS.EVENT_FETCH,
);
// Always try as pubkey (profile event) as well
const profileFilter = { kinds: [0], authors: [cleanedQuery] };
const profileEvent = await fetchEventWithFallback(
get(ndkInstance),
ndk,
profileFilter,
TIMEOUTS.EVENT_FETCH,
);
@ -196,7 +194,7 @@ export async function searchEvent(query: string): Promise<NDKEvent | null> { @@ -196,7 +194,7 @@ export async function searchEvent(query: string): Promise<NDKEvent | null> {
try {
const event = await fetchEventWithFallback(
get(ndkInstance),
ndk,
filterOrId,
TIMEOUTS.EVENT_FETCH,
);
@ -218,6 +216,7 @@ export async function searchEvent(query: string): Promise<NDKEvent | null> { @@ -218,6 +216,7 @@ export async function searchEvent(query: string): Promise<NDKEvent | null> {
*/
export async function searchNip05(
nip05Address: string,
ndk: NDK,
): Promise<NDKEvent | null> {
// NIP-05 address pattern: user@domain
if (!isValidNip05Address(nip05Address)) {
@ -239,7 +238,7 @@ export async function searchNip05( @@ -239,7 +238,7 @@ export async function searchNip05(
if (pubkey) {
const profileFilter = { kinds: [0], authors: [pubkey] };
const profileEvent = await fetchEventWithFallback(
get(ndkInstance),
ndk,
profileFilter,
TIMEOUTS.EVENT_FETCH,
);
@ -270,6 +269,7 @@ export async function searchNip05( @@ -270,6 +269,7 @@ export async function searchNip05(
*/
export async function findContainingIndexEvents(
contentEvent: NDKEvent,
ndk: NDK,
): Promise<NDKEvent[]> {
// Support all content event kinds that can be contained in indexes
const contentEventKinds = [30041, 30818, 30040, 30023];
@ -278,8 +278,6 @@ export async function findContainingIndexEvents( @@ -278,8 +278,6 @@ export async function findContainingIndexEvents(
}
try {
const ndk = get(ndkInstance);
// Search for 30040 events that reference this content event
// We need to search for events that have an 'a' tag or 'e' tag referencing this event
const contentEventId = contentEvent.id;

26
src/lib/utils/kind24_utils.ts

@ -1,12 +1,7 @@ @@ -1,12 +1,7 @@
import { get } from "svelte/store";
import { ndkInstance } from "../ndk";
import { userStore } from "../stores/userStore";
import { NDKEvent, NDKRelaySet, NDKUser } from "@nostr-dev-kit/ndk";
import type NDK from "@nostr-dev-kit/ndk";
import { nip19 } from "nostr-tools";
import NDK, { NDKEvent, NDKRelaySet } from "@nostr-dev-kit/ndk";
import { createSignedEvent } from "./nostrEventService.ts";
import { anonymousRelays } from "../consts";
import { buildCompleteRelaySet } from "./relay_management";
import { anonymousRelays } from "../consts.ts";
import { buildCompleteRelaySet } from "./relay_management.ts";
// AI-NOTE: Using existing relay utilities from relay_management.ts instead of duplicating functionality
@ -19,12 +14,8 @@ import { buildCompleteRelaySet } from "./relay_management"; @@ -19,12 +14,8 @@ import { buildCompleteRelaySet } from "./relay_management";
export async function getKind24RelaySet(
senderPubkey: string,
recipientPubkey: string,
ndk: NDK,
): Promise<string[]> {
const ndk = get(ndkInstance);
if (!ndk) {
throw new Error("NDK not available");
}
const senderPrefix = senderPubkey.slice(0, 8);
const recipientPrefix = recipientPubkey.slice(0, 8);
@ -44,13 +35,13 @@ export async function getKind24RelaySet( @@ -44,13 +35,13 @@ export async function getKind24RelaySet(
const recipientInboxRelays = recipientRelaySet.inboxRelays;
// Prioritize common relays for better privacy
const commonRelays = senderOutboxRelays.filter((relay) =>
const commonRelays = senderOutboxRelays.filter((relay: any) =>
recipientInboxRelays.includes(relay)
);
const senderOnlyRelays = senderOutboxRelays.filter((relay) =>
const senderOnlyRelays = senderOutboxRelays.filter((relay: any) =>
!recipientInboxRelays.includes(relay)
);
const recipientOnlyRelays = recipientInboxRelays.filter((relay) =>
const recipientOnlyRelays = recipientInboxRelays.filter((relay: any) =>
!senderOutboxRelays.includes(relay)
);
@ -85,11 +76,11 @@ export async function getKind24RelaySet( @@ -85,11 +76,11 @@ export async function getKind24RelaySet(
export async function createKind24Reply(
content: string,
recipientPubkey: string,
ndk: NDK,
originalEvent?: NDKEvent,
): Promise<
{ success: boolean; eventId?: string; error?: string; relays?: string[] }
> {
const ndk = get(ndkInstance);
if (!ndk?.activeUser) {
return { success: false, error: "Not logged in" };
}
@ -103,6 +94,7 @@ export async function createKind24Reply( @@ -103,6 +94,7 @@ export async function createKind24Reply(
const targetRelays = await getKind24RelaySet(
ndk.activeUser.pubkey,
recipientPubkey,
ndk,
);
if (targetRelays.length === 0) {

8
src/lib/utils/nostrEventService.ts

@ -1,11 +1,9 @@ @@ -1,11 +1,9 @@
import { nip19 } from "nostr-tools";
import { getEventHash, prefixNostrAddresses, signEvent } from "./nostrUtils.ts";
import { get } from "svelte/store";
import { goto } from "$app/navigation";
import { EVENT_KINDS, TIME_CONSTANTS } from "./search_constants.ts";
import { EXPIRATION_DURATION } from "../consts.ts";
import { ndkInstance } from "../ndk.ts";
import { NDKEvent, NDKRelaySet } from "@nostr-dev-kit/ndk";
import NDK, { NDKEvent, NDKRelaySet } from "@nostr-dev-kit/ndk";
export interface RootEventInfo {
rootId: string;
@ -337,7 +335,7 @@ export async function createSignedEvent( @@ -337,7 +335,7 @@ export async function createSignedEvent(
created_at: Number(
Math.floor(Date.now() / TIME_CONSTANTS.UNIX_TIMESTAMP_FACTOR),
),
tags: finalTags.map((tag) => [
tags: finalTags.map((tag: any) => [
String(tag[0]),
String(tag[1]),
String(tag[2] || ""),
@ -380,9 +378,9 @@ export async function createSignedEvent( @@ -380,9 +378,9 @@ export async function createSignedEvent(
export async function publishEvent(
event: NDKEvent,
relayUrls: string[],
ndk: NDK,
): Promise<string[]> {
const successfulRelays: string[] = [];
const ndk = get(ndkInstance);
if (!ndk) {
throw new Error("NDK instance not available");

10
src/lib/utils/nostrUtils.ts

@ -1,9 +1,8 @@ @@ -1,9 +1,8 @@
import { get } from "svelte/store";
import { nip19 } from "nostr-tools";
import { ndkInstance } from "../ndk.ts";
import { npubCache } from "./npubCache.ts";
import NDK, { NDKEvent, NDKRelaySet, NDKUser } from "@nostr-dev-kit/ndk";
import type { NDKKind, NostrEvent } from "@nostr-dev-kit/ndk";
import type { NostrEvent } from "@nostr-dev-kit/ndk";
import type { Filter } from "./search_types.ts";
import {
anonymousRelays,
@ -68,6 +67,7 @@ function escapeRegExp(string: string): string { @@ -68,6 +67,7 @@ function escapeRegExp(string: string): string {
*/
export async function getUserMetadata(
identifier: string,
ndk: NDK,
force = false,
): Promise<NostrProfile> {
// Remove nostr: prefix if present
@ -89,7 +89,6 @@ export async function getUserMetadata( @@ -89,7 +89,6 @@ export async function getUserMetadata(
const fallback = { name: `${cleanId.slice(0, 8)}...${cleanId.slice(-4)}` };
try {
const ndk = get(ndkInstance);
if (!ndk) {
console.warn("getUserMetadata: No NDK instance available");
npubCache.set(cleanId, fallback);
@ -176,8 +175,8 @@ export function createProfileLink( @@ -176,8 +175,8 @@ export function createProfileLink(
export async function createProfileLinkWithVerification(
identifier: string,
displayText: string | undefined,
ndk: NDK,
): Promise<string> {
const ndk = get(ndkInstance) as NDK;
if (!ndk) {
return createProfileLink(identifier, displayText);
}
@ -272,6 +271,7 @@ function createNoteLink(identifier: string): string { @@ -272,6 +271,7 @@ function createNoteLink(identifier: string): string {
*/
export async function processNostrIdentifiers(
content: string,
ndk: NDK,
): Promise<string> {
let processedContent = content;
@ -294,7 +294,7 @@ export async function processNostrIdentifiers( @@ -294,7 +294,7 @@ export async function processNostrIdentifiers(
if (!identifier.startsWith("nostr:")) {
identifier = "nostr:" + identifier;
}
const metadata = await getUserMetadata(identifier);
const metadata = await getUserMetadata(identifier, ndk);
const displayText = metadata.displayName || metadata.name;
const link = createProfileLink(identifier, displayText);
// Replace all occurrences of this exact match

14
src/lib/utils/profileCache.ts

@ -1,6 +1,4 @@ @@ -1,6 +1,4 @@
import type { NDKEvent } from "@nostr-dev-kit/ndk";
import { ndkInstance } from "$lib/ndk";
import { get } from "svelte/store";
import NDK, { type NDKEvent } from "@nostr-dev-kit/ndk";
import { nip19 } from "nostr-tools";
interface ProfileData {
@ -18,9 +16,8 @@ const profileCache = new Map<string, ProfileData>(); @@ -18,9 +16,8 @@ const profileCache = new Map<string, ProfileData>();
* @param pubkey - The public key to fetch profile for
* @returns Profile data or null if not found
*/
async function fetchProfile(pubkey: string): Promise<ProfileData | null> {
async function fetchProfile(pubkey: string, ndk: NDK): Promise<ProfileData | null> {
try {
const ndk = get(ndkInstance);
const profileEvents = await ndk.fetchEvents({
kinds: [0],
authors: [pubkey],
@ -52,7 +49,7 @@ async function fetchProfile(pubkey: string): Promise<ProfileData | null> { @@ -52,7 +49,7 @@ async function fetchProfile(pubkey: string): Promise<ProfileData | null> {
* @param pubkey - The public key to get display name for
* @returns Display name, name, or shortened pubkey
*/
export async function getDisplayName(pubkey: string): Promise<string> {
export async function getDisplayName(pubkey: string, ndk: NDK): Promise<string> {
// Check cache first
if (profileCache.has(pubkey)) {
const profile = profileCache.get(pubkey)!;
@ -60,7 +57,7 @@ export async function getDisplayName(pubkey: string): Promise<string> { @@ -60,7 +57,7 @@ export async function getDisplayName(pubkey: string): Promise<string> {
}
// Fetch profile
const profile = await fetchProfile(pubkey);
const profile = await fetchProfile(pubkey, ndk);
if (profile) {
profileCache.set(pubkey, profile);
return profile.display_name || profile.name || shortenPubkey(pubkey);
@ -78,6 +75,7 @@ export async function getDisplayName(pubkey: string): Promise<string> { @@ -78,6 +75,7 @@ export async function getDisplayName(pubkey: string): Promise<string> {
*/
export async function batchFetchProfiles(
pubkeys: string[],
ndk: NDK,
onProgress?: (fetched: number, total: number) => void,
): Promise<NDKEvent[]> {
const allProfileEvents: NDKEvent[] = [];
@ -91,8 +89,6 @@ export async function batchFetchProfiles( @@ -91,8 +89,6 @@ export async function batchFetchProfiles(
}
try {
const ndk = get(ndkInstance);
// Report initial progress
const cachedCount = pubkeys.length - uncachedPubkeys.length;
if (onProgress) onProgress(cachedCount, pubkeys.length);

15
src/lib/utils/profile_search.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { activeInboxRelays, ndkInstance } from "../ndk.ts";
import { activeInboxRelays } from "../ndk.ts";
import { getNpubFromNip05, getUserMetadata } from "./nostrUtils.ts";
import NDK, { NDKEvent, NDKRelaySet } from "@nostr-dev-kit/ndk";
import { searchCache } from "./searchCache.ts";
@ -17,6 +17,7 @@ import { @@ -17,6 +17,7 @@ import {
*/
export async function searchProfiles(
searchTerm: string,
ndk: NDK,
): Promise<ProfileSearchResult> {
const normalizedSearchTerm = normalizeSearchTerm(searchTerm);
@ -46,7 +47,6 @@ export async function searchProfiles( @@ -46,7 +47,6 @@ export async function searchProfiles(
return { profiles, Status: {} };
}
const ndk = get(ndkInstance);
if (!ndk) {
console.error("NDK not initialized");
throw new Error("NDK not initialized");
@ -63,7 +63,7 @@ export async function searchProfiles( @@ -63,7 +63,7 @@ export async function searchProfiles(
normalizedSearchTerm.startsWith("nprofile")
) {
try {
const metadata = await getUserMetadata(normalizedSearchTerm);
const metadata = await getUserMetadata(normalizedSearchTerm, ndk);
if (metadata) {
foundProfiles = [metadata];
}
@ -76,7 +76,7 @@ export async function searchProfiles( @@ -76,7 +76,7 @@ export async function searchProfiles(
try {
const npub = await getNpubFromNip05(normalizedNip05);
if (npub) {
const metadata = await getUserMetadata(npub);
const metadata = await getUserMetadata(npub, ndk);
const profile: NostrProfile = {
...metadata,
pubkey: npub,
@ -89,7 +89,7 @@ export async function searchProfiles( @@ -89,7 +89,7 @@ export async function searchProfiles(
} else {
// Try NIP-05 search first (faster than relay search)
console.log("Starting NIP-05 search for:", normalizedSearchTerm);
foundProfiles = await searchNip05Domains(normalizedSearchTerm);
foundProfiles = await searchNip05Domains(normalizedSearchTerm, ndk);
console.log(
"NIP-05 search completed, found:",
foundProfiles.length,
@ -142,6 +142,7 @@ export async function searchProfiles( @@ -142,6 +142,7 @@ export async function searchProfiles(
*/
async function searchNip05Domains(
searchTerm: string,
ndk: NDK,
): Promise<NostrProfile[]> {
const foundProfiles: NostrProfile[] = [];
@ -184,7 +185,7 @@ async function searchNip05Domains( @@ -184,7 +185,7 @@ async function searchNip05Domains(
"NIP-05 search: SUCCESS! found npub for gitcitadel.com:",
npub,
);
const metadata = await getUserMetadata(npub);
const metadata = await getUserMetadata(npub, ndk);
const profile: NostrProfile = {
...metadata,
pubkey: npub,
@ -216,7 +217,7 @@ async function searchNip05Domains( @@ -216,7 +217,7 @@ async function searchNip05Domains(
const npub = await getNpubFromNip05(nip05Address);
if (npub) {
console.log("NIP-05 search: found npub for", nip05Address, ":", npub);
const metadata = await getUserMetadata(npub);
const metadata = await getUserMetadata(npub, ndk);
const profile: NostrProfile = {
...metadata,
pubkey: npub,

28
src/lib/utils/subscription_search.ts

@ -1,10 +1,9 @@ @@ -1,10 +1,9 @@
// deno-lint-ignore-file no-explicit-any
import { ndkInstance } from "../ndk.ts";
import { getMatchingTags, getNpubFromNip05 } from "./nostrUtils.ts";
import { nip19 } from "./nostrUtils.ts";
import { NDKEvent, NDKRelaySet } from "@nostr-dev-kit/ndk";
import NDK, { NDKEvent, NDKRelaySet } from "@nostr-dev-kit/ndk";
import { searchCache } from "./searchCache.ts";
import { communityRelays, searchRelays } from "../consts.ts";
import { searchRelays } from "../consts.ts";
import { get } from "svelte/store";
import type {
SearchCallbacks,
@ -43,6 +42,7 @@ function filterUnwantedEvents(events: NDKEvent[]): NDKEvent[] { @@ -43,6 +42,7 @@ function filterUnwantedEvents(events: NDKEvent[]): NDKEvent[] {
export async function searchBySubscription(
searchType: SearchSubscriptionType,
searchTerm: string,
ndk: NDK,
callbacks?: SearchCallbacks,
abortSignal?: AbortSignal,
): Promise<SearchResult> {
@ -71,7 +71,6 @@ export async function searchBySubscription( @@ -71,7 +71,6 @@ export async function searchBySubscription(
}
}
const ndk = get(ndkInstance);
if (!ndk) {
console.error("subscription_search: NDK not initialized");
throw new Error("NDK not initialized");
@ -158,6 +157,7 @@ export async function searchBySubscription( @@ -158,6 +157,7 @@ export async function searchBySubscription(
searchType,
searchFilter,
searchState,
ndk,
callbacks,
cleanup,
);
@ -174,6 +174,7 @@ export async function searchBySubscription( @@ -174,6 +174,7 @@ export async function searchBySubscription(
searchType,
searchFilter,
searchState,
ndk,
callbacks,
cleanup,
);
@ -278,6 +279,7 @@ export async function searchBySubscription( @@ -278,6 +279,7 @@ export async function searchBySubscription(
searchType,
searchFilter,
searchState,
ndk,
callbacks,
cleanup,
);
@ -435,7 +437,7 @@ async function createProfileSearchFilter( @@ -435,7 +437,7 @@ async function createProfileSearchFilter(
*/
function createPrimaryRelaySet(
searchType: SearchSubscriptionType,
ndk: any,
ndk: NDK,
): NDKRelaySet {
// Debug: Log all relays in NDK pool
const poolRelays = Array.from(ndk.pool.relays.values());
@ -685,11 +687,10 @@ function searchOtherRelaysInBackground( @@ -685,11 +687,10 @@ function searchOtherRelaysInBackground(
searchType: SearchSubscriptionType,
searchFilter: SearchFilter,
searchState: any,
ndk: NDK,
callbacks?: SearchCallbacks,
cleanup?: () => void,
): Promise<SearchResult> {
const ndk = get(ndkInstance);
// AI-NOTE: 2025-01-24 - Use ALL available relays for comprehensive search coverage
// This ensures we don't miss events that might be on any available relay
const otherRelays = new NDKRelaySet(
@ -740,6 +741,7 @@ function searchOtherRelaysInBackground( @@ -740,6 +741,7 @@ function searchOtherRelaysInBackground(
searchType,
searchState,
searchFilter,
ndk,
callbacks,
);
searchCache.set(searchType, searchState.normalizedSearchTerm, result);
@ -756,12 +758,13 @@ function processEoseResults( @@ -756,12 +758,13 @@ function processEoseResults(
searchType: SearchSubscriptionType,
searchState: any,
searchFilter: SearchFilter,
ndk: NDK,
callbacks?: SearchCallbacks,
): SearchResult {
if (searchType === "n") {
return processProfileEoseResults(searchState, searchFilter, callbacks);
return processProfileEoseResults(searchState, searchFilter, ndk, callbacks);
} else if (searchType === "d") {
return processContentEoseResults(searchState, searchType);
return processContentEoseResults(searchState, searchType, ndk);
} else if (searchType === "t") {
return processTTagEoseResults(searchState);
}
@ -775,6 +778,7 @@ function processEoseResults( @@ -775,6 +778,7 @@ function processEoseResults(
function processProfileEoseResults(
searchState: any,
searchFilter: SearchFilter,
ndk: NDK,
callbacks?: SearchCallbacks,
): SearchResult {
if (searchState.foundProfiles.length === 0) {
@ -812,6 +816,7 @@ function processProfileEoseResults( @@ -812,6 +816,7 @@ function processProfileEoseResults(
dedupedProfiles,
new Set(),
new Set(),
ndk,
targetPubkey,
callbacks,
);
@ -833,6 +838,7 @@ function processProfileEoseResults( @@ -833,6 +838,7 @@ function processProfileEoseResults(
dedupedProfiles,
new Set(),
new Set(),
ndk,
profile.pubkey,
callbacks,
);
@ -862,6 +868,7 @@ function processProfileEoseResults( @@ -862,6 +868,7 @@ function processProfileEoseResults(
function processContentEoseResults(
searchState: any,
searchType: SearchSubscriptionType,
ndk: NDK,
): SearchResult {
if (searchState.firstOrderEvents.length === 0) {
return createEmptySearchResult(
@ -889,6 +896,7 @@ function processContentEoseResults( @@ -889,6 +896,7 @@ function processContentEoseResults(
dedupedEvents,
searchState.eventIds,
searchState.eventAddresses,
ndk,
);
}
@ -948,6 +956,7 @@ async function performSecondOrderSearchInBackground( @@ -948,6 +956,7 @@ async function performSecondOrderSearchInBackground(
firstOrderEvents: NDKEvent[],
eventIds: Set<string> = new Set(),
addresses: Set<string> = new Set(),
ndk: NDK,
targetPubkey?: string,
callbacks?: SearchCallbacks,
) {
@ -958,7 +967,6 @@ async function performSecondOrderSearchInBackground( @@ -958,7 +967,6 @@ async function performSecondOrderSearchInBackground(
"with targetPubkey:",
targetPubkey,
);
const ndk = get(ndkInstance);
let allSecondOrderEvents: NDKEvent[] = [];
// Set a timeout for second-order search

11
src/lib/utils/tag_event_fetch.ts

@ -1,7 +1,5 @@ @@ -1,7 +1,5 @@
import type { NDKEvent } from "@nostr-dev-kit/ndk";
import { ndkInstance } from "../ndk";
import { get } from "svelte/store";
import { batchFetchProfiles, extractPubkeysFromEvents } from "./profileCache";
import NDK, { type NDKEvent } from "@nostr-dev-kit/ndk";
import { batchFetchProfiles, extractPubkeysFromEvents } from "./profileCache.ts";
// Constants for publication event kinds
const INDEX_EVENT_KIND = 30040;
@ -33,6 +31,7 @@ export async function fetchTaggedEventsFromRelays( @@ -33,6 +31,7 @@ export async function fetchTaggedEventsFromRelays(
tags: string[],
existingEventIds: Set<string>,
baseEvents: NDKEvent[],
ndk: NDK,
debug?: (...args: any[]) => void,
): Promise<TagExpansionResult> {
const log = debug || console.debug;
@ -40,7 +39,6 @@ export async function fetchTaggedEventsFromRelays( @@ -40,7 +39,6 @@ export async function fetchTaggedEventsFromRelays(
log("Fetching from relays for tags:", tags);
// Fetch publications that have any of the specified tags
const ndk = get(ndkInstance);
const taggedPublications = await ndk.fetchEvents({
kinds: [INDEX_EVENT_KIND],
"#t": tags, // Match any of these tags
@ -188,6 +186,7 @@ export function findTaggedEventsInFetched( @@ -188,6 +186,7 @@ export function findTaggedEventsInFetched(
export async function fetchProfilesForNewEvents(
newPublications: NDKEvent[],
newContentEvents: NDKEvent[],
ndk: NDK,
onProgressUpdate: (
progress: { current: number; total: number } | null,
) => void,
@ -210,7 +209,7 @@ export async function fetchProfilesForNewEvents( @@ -210,7 +209,7 @@ export async function fetchProfilesForNewEvents(
onProgressUpdate({ current: 0, total: newPubkeys.size });
await batchFetchProfiles(Array.from(newPubkeys), (fetched, total) => {
await batchFetchProfiles(Array.from(newPubkeys), ndk, (fetched, total) => {
onProgressUpdate({ current: fetched, total });
});

29
src/routes/+layout.svelte

@ -1,15 +1,16 @@ @@ -1,15 +1,16 @@
<script lang="ts">
import "../app.css";
import Navigation from "$lib/components/Navigation.svelte";
import { onMount } from "svelte";
import { onMount, setContext } from "svelte";
import { page } from "$app/stores";
import { goto } from "$app/navigation";
import { Alert } from "flowbite-svelte";
import { HammerSolid } from "flowbite-svelte-icons";
import { logCurrentRelayConfiguration, activeInboxRelays, activeOutboxRelays, cleanupNdk } from "$lib/ndk";
import { cleanupNdk } from "$lib/ndk";
import type { LayoutProps } from "./$types";
// Define children prop for Svelte 5
let { children } = $props();
let { data, children }: LayoutProps = $props();
setContext("ndk", data.ndk);
// Get standard metadata for OpenGraph tags
let title = "Library of Alexandria";
@ -20,24 +21,6 @@ @@ -20,24 +21,6 @@
let summary =
"Alexandria is a digital library, utilizing Nostr events for curated publications and wiki pages.";
// AI-NOTE: Refactored to avoid blocking $effect with logging operations
// Reactive effect to log relay configuration when stores change - non-blocking approach
$effect.pre(() => {
const inboxRelays = $activeInboxRelays;
const outboxRelays = $activeOutboxRelays;
// Only log if we have relays (not empty arrays)
if (inboxRelays.length > 0 || outboxRelays.length > 0) {
// Defer logging to avoid blocking the reactive system
requestAnimationFrame(() => {
console.log('🔌 Relay Configuration Updated:');
console.log('📥 Inbox Relays:', inboxRelays);
console.log('📤 Outbox Relays:', outboxRelays);
console.log(`📊 Total: ${inboxRelays.length} inbox, ${outboxRelays.length} outbox`);
});
}
});
onMount(() => {
const rect = document.body.getBoundingClientRect();
// document.body.style.height = `${rect.height}px`;

8
src/routes/+layout.ts

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
import type { LayoutLoad } from "./$types";
import { initNdk } from "$lib/ndk";
export const load: LayoutLoad = () => {
return {
ndk: initNdk(),
};
}

7
src/routes/contact/+page.svelte

@ -9,9 +9,9 @@ @@ -9,9 +9,9 @@
Input,
Modal,
} from "flowbite-svelte";
import { ndkInstance, ndkSignedIn, activeInboxRelays, activeOutboxRelays } from "$lib/ndk";
import { activeInboxRelays, activeOutboxRelays, getNdkContext } from "$lib/ndk";
import { userStore } from "$lib/stores/userStore";
import { communityRelays, anonymousRelays } from "$lib/consts";
import { anonymousRelays } from "$lib/consts";
import type NDK from "@nostr-dev-kit/ndk";
import { NDKEvent, NDKRelaySet } from "@nostr-dev-kit/ndk";
// @ts-ignore - Workaround for Svelte component import issue
@ -21,6 +21,8 @@ @@ -21,6 +21,8 @@
import { getMimeTags } from "$lib/utils/mime";
import { userBadge } from "$lib/snippets/UserSnippets.svelte";
const ndk = getNdkContext();
// Function to close the success message
function closeSuccessMessage() {
submissionSuccess = false;
@ -193,7 +195,6 @@ @@ -193,7 +195,6 @@
try {
// Get NDK instance
const ndk = $ndkInstance;
if (!ndk) {
throw new Error("NDK instance not available");
}

6
src/routes/my-notes/+page.svelte

@ -1,14 +1,15 @@ @@ -1,14 +1,15 @@
<script lang="ts">
import { onMount } from "svelte";
import { goto } from "$app/navigation";
import { userStore } from "$lib/stores/userStore";
import { ndkInstance } from "$lib/ndk";
import type { NDKEvent } from "@nostr-dev-kit/ndk";
import { get } from "svelte/store";
import { getMatchingTags } from "$lib/utils/nostrUtils";
import { getTitleTagForEvent } from "$lib/utils/event_input_utils";
import asciidoctor from "asciidoctor";
import { postProcessAsciidoctorHtml } from "$lib/utils/markup/asciidoctorPostProcessor";
import { getNdkContext } from "$lib/ndk";
const ndk = getNdkContext();
let events: NDKEvent[] = $state([]);
let loading = $state(true);
@ -42,7 +43,6 @@ @@ -42,7 +43,6 @@
loading = false;
return;
}
const ndk = get(ndkInstance);
if (!ndk) {
error = "NDK not initialized.";
loading = false;

10
src/routes/new/edit/+page.svelte

@ -4,7 +4,6 @@ @@ -4,7 +4,6 @@
Textarea,
Toolbar,
ToolbarButton,
Tooltip,
} from "flowbite-svelte";
import {
CodeOutline,
@ -13,8 +12,11 @@ @@ -13,8 +12,11 @@
} from "flowbite-svelte-icons";
import Preview from "$lib/components/Preview.svelte";
import Pharos, { pharosInstance } from "$lib/parser";
import { ndkInstance } from "$lib/ndk";
import { goto } from "$app/navigation";
import { getNdkContext } from "$lib/ndk";
const ndk = getNdkContext();
let someIndexValue = 0;
// TODO: Prompt user to sign in before editing.
@ -26,7 +28,7 @@ @@ -26,7 +28,7 @@
const showPreview = () => {
try {
$pharosInstance ??= new Pharos($ndkInstance);
$pharosInstance ??= new Pharos(ndk);
$pharosInstance.reset();
$pharosInstance.parse(editorText);
} catch (e) {
@ -53,7 +55,7 @@ @@ -53,7 +55,7 @@
return;
}
$pharosInstance.generate($ndkInstance.activeUser?.pubkey!);
$pharosInstance.generate(ndk.activeUser?.pubkey!);
goto("/new/compose");
};
</script>

2
src/routes/publication/+page.server.ts

@ -17,7 +17,7 @@ const IDENTIFIER_PREFIXES = { @@ -17,7 +17,7 @@ const IDENTIFIER_PREFIXES = {
NEVENT: "nevent",
} as const;
export const load: PageServerLoad = ({ url }) => {
export const load: PageServerLoad = ({ url }: { url: URL }) => {
const id = url.searchParams.get("id");
const dTag = url.searchParams.get("d");

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

@ -1,38 +1,11 @@ @@ -1,38 +1,11 @@
import { error } from "@sveltejs/kit";
import type { LayoutServerLoad } from "./$types";
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 }) => {
const { type, identifier } = params;
// Try to fetch event server-side for metadata
const indexEvent = await fetchEventServerSide(type, identifier);
// 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";
export const load: LayoutServerLoad = ({ url }: { url: URL }) => {
const currentUrl = `${url.origin}${url.pathname}`;
return {
indexEvent, // Will be null, triggering client-side fetch
metadata: {
title,
summary,
image,
currentUrl,
},
};

17
src/routes/publication/[type]/[identifier]/+page.ts

@ -9,16 +9,12 @@ import { @@ -9,16 +9,12 @@ import {
import type { NostrEvent } from "../../../../lib/utils/websocket_utils.ts";
export const load: PageLoad = async (
{ params, parent }: {
{ params }: {
params: { type: string; identifier: string };
parent: any;
},
) => {
const { type, identifier } = params;
// Get layout data (no server-side data since SSR is disabled)
const layoutData = await parent();
// AI-NOTE: Always fetch client-side since server-side fetch returns null for now
let indexEvent: NostrEvent | null = null;
@ -74,20 +70,9 @@ export const load: PageLoad = async ( @@ -74,20 +70,9 @@ export const load: PageLoad = async (
const publicationType =
indexEvent.tags.find((tag) => tag[0] === "type")?.[1] ?? "";
// AI-NOTE: Use proper NDK instance from layout or create one with relays
let ndk = layoutData?.ndk;
if (!ndk) {
// 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();
}
const result = {
publicationType,
indexEvent,
ndk, // Use minimal NDK instance
};
return result;

21
src/routes/visualize/+page.svelte

@ -8,7 +8,6 @@ @@ -8,7 +8,6 @@
import { onMount } from "svelte";
import { get } from "svelte/store";
import EventNetwork from "$lib/navigator/EventNetwork/index.svelte";
import { ndkInstance } from "$lib/ndk";
import type { NDKEvent } from "@nostr-dev-kit/ndk";
import { filterValidIndexEvents } from "$lib/utils";
import { networkFetchLimit } from "$lib/state";
@ -17,7 +16,7 @@ @@ -17,7 +16,7 @@
import type { PageData } from './$types';
import { getEventKindColor, getEventKindName } from "$lib/utils/eventColors";
import { extractPubkeysFromEvents, batchFetchProfiles } from "$lib/utils/profileCache";
import { activePubkey } from "$lib/ndk";
import { activePubkey, getNdkContext } 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
@ -29,6 +28,8 @@ @@ -29,6 +28,8 @@
import { deduplicateAndCombineEvents } from "$lib/utils/eventDeduplication";
import type { EventCounts } from "$lib/types";
const ndk = getNdkContext();
// Configuration
const DEBUG = true; // Set to true to enable debug logging
const INDEX_EVENT_KIND = 30040;
@ -130,7 +131,7 @@ @@ -130,7 +131,7 @@
// If limit is 1, only fetch the current user's follow list
if (config.limit === 1) {
const userFollowList = await $ndkInstance.fetchEvents({
const userFollowList = await ndk.fetchEvents({
kinds: [3],
authors: [currentUserPubkey],
limit: 1
@ -148,7 +149,7 @@ @@ -148,7 +149,7 @@
debug(`Fetched user's follow list`);
} else {
// If limit > 1, fetch the user's follow list plus additional ones from people they follow
const userFollowList = await $ndkInstance.fetchEvents({
const userFollowList = await ndk.fetchEvents({
kinds: [3],
authors: [currentUserPubkey],
limit: 1
@ -180,7 +181,7 @@ @@ -180,7 +181,7 @@
debug(`Fetching ${pubkeysToFetch.length} additional follow lists (total limit: ${config.limit})`);
const additionalFollowLists = await $ndkInstance.fetchEvents({
const additionalFollowLists = await ndk.fetchEvents({
kinds: [3],
authors: pubkeysToFetch
});
@ -215,7 +216,7 @@ @@ -215,7 +216,7 @@
debug(`Fetching level ${level} follow lists for ${currentLevelPubkeys.length} pubkeys`);
// Fetch follow lists for this level
const levelFollowLists = await $ndkInstance.fetchEvents({
const levelFollowLists = await ndk.fetchEvents({
kinds: [3],
authors: currentLevelPubkeys
});
@ -362,7 +363,7 @@ @@ -362,7 +363,7 @@
const followEvents = await fetchFollowLists(config);
allFetchedEvents.push(...followEvents);
} else {
const fetchedEvents = await $ndkInstance.fetchEvents(
const fetchedEvents = await ndk.fetchEvents(
{
kinds: [config.kind],
limit: config.limit
@ -394,7 +395,7 @@ @@ -394,7 +395,7 @@
if (data.eventId) {
// Fetch specific publication
debug(`Fetching specific publication: ${data.eventId}`);
const event = await $ndkInstance.fetchEvent(data.eventId);
const event = await ndk.fetchEvent(data.eventId);
if (!event) {
throw new Error(`Publication not found: ${data.eventId}`);
@ -414,7 +415,7 @@ @@ -414,7 +415,7 @@
const indexConfig = publicationConfigs.find(ec => ec.kind === INDEX_EVENT_KIND);
const indexLimit = indexConfig?.limit || 20;
const indexEvents = await $ndkInstance.fetchEvents(
const indexEvents = await ndk.fetchEvents(
{
kinds: [INDEX_EVENT_KIND],
limit: indexLimit
@ -455,7 +456,7 @@ @@ -455,7 +456,7 @@
const contentEventPromises = Array.from(referencesByAuthor.entries()).map(
async ([author, refs]) => {
const dTags = [...new Set(refs.map(r => r.dTag))]; // Dedupe d-tags
return $ndkInstance.fetchEvents({
return ndk.fetchEvents({
kinds: enabledContentKinds, // Only fetch enabled kinds
authors: [author],
"#d": dTags,

5
src/styles/events.css

@ -1,5 +0,0 @@ @@ -1,5 +0,0 @@
@layer components {
canvas.qr-code {
@apply block mx-auto my-4;
}
}

9
tests/unit/eventInput30040.test.ts

@ -3,10 +3,6 @@ import { @@ -3,10 +3,6 @@ import {
build30040EventSet,
validate30040EventSet,
} from "../../src/lib/utils/event_input_utils";
import {
extractDocumentMetadata,
parseAsciiDocWithMetadata,
} from "../../src/lib/utils/asciidoc_metadata";
// Mock NDK and other dependencies
vi.mock("@nostr-dev-kit/ndk", () => ({
@ -22,6 +18,7 @@ vi.mock("@nostr-dev-kit/ndk", () => ({ @@ -22,6 +18,7 @@ vi.mock("@nostr-dev-kit/ndk", () => ({
})),
}));
// TODO: Replace with getNdkContext mock.
vi.mock("../../src/lib/ndk", () => ({
ndkInstance: {
subscribe: vi.fn(),
@ -265,7 +262,7 @@ This is the preamble content. @@ -265,7 +262,7 @@ This is the preamble content.
expect(sectionEvents).toHaveLength(3);
// All sections should have empty content
sectionEvents.forEach((section, index) => {
sectionEvents.forEach((section: any, index: number) => {
expect(section.kind).toBe(30041);
expect(section.content).toBe("");
expect(section.tags).toContainEqual([
@ -320,7 +317,7 @@ This is the preamble content. @@ -320,7 +317,7 @@ This is the preamble content.
expect(sectionEvents).toHaveLength(3);
// All sections should have empty content
sectionEvents.forEach((section, index) => {
sectionEvents.forEach((section: any, index: number) => {
expect(section.kind).toBe(30041);
expect(section.content).toBe("");
expect(section.tags).toContainEqual([

38
tests/unit/tagExpansion.test.ts

@ -4,7 +4,6 @@ import { @@ -4,7 +4,6 @@ import {
fetchProfilesForNewEvents,
fetchTaggedEventsFromRelays,
findTaggedEventsInFetched,
type TagExpansionResult,
} from "../../src/lib/utils/tag_event_fetch";
// Mock NDKEvent for testing
@ -48,6 +47,7 @@ const mockNDK = { @@ -48,6 +47,7 @@ const mockNDK = {
};
// Mock the ndkInstance store
// TODO: Replace with getNdkContext mock.
vi.mock("../../src/lib/ndk", () => ({
ndkInstance: {
subscribe: vi.fn((fn) => {
@ -179,8 +179,8 @@ describe("Tag Expansion Tests", () => { @@ -179,8 +179,8 @@ describe("Tag Expansion Tests", () => {
// Should return the matching publications
expect(result.publications).toHaveLength(2);
expect(result.publications.map((p) => p.id)).toContain("pub1");
expect(result.publications.map((p) => p.id)).toContain("pub2");
expect(result.publications.map((p: any) => p.id)).toContain("pub1");
expect(result.publications.map((p: any) => p.id)).toContain("pub2");
// Should fetch content events for the publications
expect(mockNDK.fetchEvents).toHaveBeenCalledWith({
@ -210,9 +210,9 @@ describe("Tag Expansion Tests", () => { @@ -210,9 +210,9 @@ describe("Tag Expansion Tests", () => {
// Should exclude pub1 since it already exists
expect(result.publications).toHaveLength(2);
expect(result.publications.map((p) => p.id)).not.toContain("pub1");
expect(result.publications.map((p) => p.id)).toContain("pub2");
expect(result.publications.map((p) => p.id)).toContain("pub3");
expect(result.publications.map((p: any) => p.id)).not.toContain("pub1");
expect(result.publications.map((p: any) => p.id)).toContain("pub2");
expect(result.publications.map((p: any) => p.id)).toContain("pub3");
});
it("should handle empty tag array gracefully", async () => {
@ -251,15 +251,15 @@ describe("Tag Expansion Tests", () => { @@ -251,15 +251,15 @@ describe("Tag Expansion Tests", () => {
// Should find publications with bitcoin tag
expect(result.publications).toHaveLength(2);
expect(result.publications.map((p) => p.id)).toContain("pub1");
expect(result.publications.map((p) => p.id)).toContain("pub2");
expect(result.publications.map((p: any) => p.id)).toContain("pub1");
expect(result.publications.map((p: any) => p.id)).toContain("pub2");
// Should find content events for those publications
expect(result.contentEvents).toHaveLength(4);
expect(result.contentEvents.map((c) => c.id)).toContain("content1");
expect(result.contentEvents.map((c) => c.id)).toContain("content2");
expect(result.contentEvents.map((c) => c.id)).toContain("content3");
expect(result.contentEvents.map((c) => c.id)).toContain("content4");
expect(result.contentEvents.map((c: any) => c.id)).toContain("content1");
expect(result.contentEvents.map((c: any) => c.id)).toContain("content2");
expect(result.contentEvents.map((c: any) => c.id)).toContain("content3");
expect(result.contentEvents.map((c: any) => c.id)).toContain("content4");
});
it("should exclude base events from search results", () => {
@ -277,8 +277,8 @@ describe("Tag Expansion Tests", () => { @@ -277,8 +277,8 @@ describe("Tag Expansion Tests", () => {
// Should exclude pub1 since it's a base event
expect(result.publications).toHaveLength(1);
expect(result.publications.map((p) => p.id)).not.toContain("pub1");
expect(result.publications.map((p) => p.id)).toContain("pub2");
expect(result.publications.map((p: any) => p.id)).not.toContain("pub1");
expect(result.publications.map((p: any) => p.id)).toContain("pub2");
});
it("should handle multiple tags (OR logic)", () => {
@ -296,9 +296,9 @@ describe("Tag Expansion Tests", () => { @@ -296,9 +296,9 @@ describe("Tag Expansion Tests", () => {
// Should find publications with either bitcoin OR ethereum tags
expect(result.publications).toHaveLength(3);
expect(result.publications.map((p) => p.id)).toContain("pub1"); // bitcoin
expect(result.publications.map((p) => p.id)).toContain("pub2"); // bitcoin
expect(result.publications.map((p) => p.id)).toContain("pub3"); // ethereum
expect(result.publications.map((p: any) => p.id)).toContain("pub1"); // bitcoin
expect(result.publications.map((p: any) => p.id)).toContain("pub2"); // bitcoin
expect(result.publications.map((p: any) => p.id)).toContain("pub3"); // ethereum
});
it("should handle events without tags gracefully", () => {
@ -324,7 +324,7 @@ describe("Tag Expansion Tests", () => { @@ -324,7 +324,7 @@ describe("Tag Expansion Tests", () => {
);
// Should not include events without tags
expect(result.publications.map((p) => p.id)).not.toContain("no-tags");
expect(result.publications.map((p: any) => p.id)).not.toContain("no-tags");
});
});
@ -497,7 +497,7 @@ describe("Tag Expansion Tests", () => { @@ -497,7 +497,7 @@ describe("Tag Expansion Tests", () => {
// Should handle d-tags with colons correctly
expect(result.publications).toHaveLength(3);
expect(result.contentEvents.map((c) => c.id)).toContain("colon-content");
expect(result.contentEvents.map((c: any) => c.id)).toContain("colon-content");
});
});
});

Loading…
Cancel
Save