diff --git a/src/lib/components/EventInput.svelte b/src/lib/components/EventInput.svelte
index 1b9cb6b..cc2e2a3 100644
--- a/src/lib/components/EventInput.svelte
+++ b/src/lib/components/EventInput.svelte
@@ -16,6 +16,7 @@
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 type { NDKEvent } from "$lib/utils/nostrUtils";
import { prefixNostrAddresses } from "$lib/utils/nostrUtils";
@@ -99,8 +100,12 @@
function validate(): { valid: boolean; reason?: string } {
const currentUserPubkey = get(userPubkey as any);
- if (!currentUserPubkey) return { valid: false, reason: "Not logged in." };
- const pubkey = String(currentUserPubkey);
+ const userState = get(userStore);
+
+ // Try userPubkey first, then fallback to userStore
+ const pubkey = currentUserPubkey || userState.pubkey;
+ if (!pubkey) return { valid: false, reason: "Not logged in." };
+
if (!content.trim()) return { valid: false, reason: "Content required." };
if (kind === 30023) {
const v = validateNotAsciidoc(content);
@@ -137,14 +142,18 @@
try {
const ndk = get(ndkInstance);
const currentUserPubkey = get(userPubkey as any);
- if (!ndk || !currentUserPubkey) {
+ const userState = get(userStore);
+
+ // Try userPubkey first, then fallback to userStore
+ const pubkey = currentUserPubkey || userState.pubkey;
+ if (!ndk || !pubkey) {
error = "NDK or pubkey missing.";
loading = false;
return;
}
- const pubkey = String(currentUserPubkey);
+ const pubkeyString = String(pubkey);
- if (!/^[a-fA-F0-9]{64}$/.test(pubkey)) {
+ if (!/^[a-fA-F0-9]{64}$/.test(pubkeyString)) {
error = "Invalid public key: must be a 64-character hex string.";
loading = false;
return;
@@ -158,7 +167,7 @@
return;
}
- const baseEvent = { pubkey, created_at: createdAt };
+ const baseEvent = { pubkey: pubkeyString, created_at: createdAt };
let events: NDKEvent[] = [];
console.log("Publishing event with kind:", kind);
@@ -235,7 +244,7 @@
kind,
content: prefixedContent,
tags: eventTags,
- pubkey,
+ pubkey: pubkeyString,
created_at: createdAt,
};
@@ -520,7 +529,7 @@
Event ID: {lastPublishedEventId}
diff --git a/src/lib/components/EventSearch.svelte b/src/lib/components/EventSearch.svelte
index cb71ade..10f888b 100644
--- a/src/lib/components/EventSearch.svelte
+++ b/src/lib/components/EventSearch.svelte
@@ -13,6 +13,8 @@
import { activeInboxRelays, activeOutboxRelays } 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";
// Props definition
let {
@@ -492,7 +494,7 @@
// Wait for relays to be available (with timeout)
let retryCount = 0;
- const maxRetries = 10; // Wait up to 5 seconds (10 * 500ms)
+ const maxRetries = 20; // Wait up to 10 seconds (20 * 500ms) for user login to complete
while ($activeInboxRelays.length === 0 && $activeOutboxRelays.length === 0 && retryCount < maxRetries) {
console.debug(`EventSearch: Waiting for relays... (attempt ${retryCount + 1}/${maxRetries})`);
@@ -500,6 +502,19 @@
retryCount++;
}
+ // Additional wait for user-specific relays if user is logged in
+ const currentUser = get(userStore);
+ if (currentUser.signedIn && currentUser.pubkey) {
+ console.debug(`EventSearch: User is logged in (${currentUser.pubkey}), waiting for user-specific relays...`);
+ retryCount = 0;
+ while ($activeOutboxRelays.length <= 9 && retryCount < maxRetries) {
+ // If we still have the default relay count (9), wait for user-specific relays
+ console.debug(`EventSearch: Waiting for user-specific relays... (attempt ${retryCount + 1}/${maxRetries})`);
+ await new Promise(resolve => setTimeout(resolve, 500));
+ retryCount++;
+ }
+ }
+
// Check if we have any relays available
if ($activeInboxRelays.length === 0 && $activeOutboxRelays.length === 0) {
console.warn("EventSearch: No relays available after waiting, failing search");
diff --git a/src/lib/components/util/Profile.svelte b/src/lib/components/util/Profile.svelte
index ad52084..cc5ff4a 100644
--- a/src/lib/components/util/Profile.svelte
+++ b/src/lib/components/util/Profile.svelte
@@ -114,12 +114,24 @@
console.log("Profile component - activeInboxRelays:", inboxRelays);
});
- // Manual trigger to refresh profile when user signs in
+ // Track if we've already refreshed the profile for this session
+ let hasRefreshedProfile = $state(false);
+
+ // Reset the refresh flag when user logs out
+ $effect(() => {
+ const currentUser = userState;
+ if (!currentUser.signedIn) {
+ hasRefreshedProfile = false;
+ }
+ });
+
+ // Manual trigger to refresh profile when user signs in (only once)
$effect(() => {
const currentUser = userState;
- if (currentUser.signedIn && currentUser.npub && !isRefreshingProfile) {
+ if (currentUser.signedIn && currentUser.npub && !isRefreshingProfile && !hasRefreshedProfile) {
console.log("Profile: User signed in, triggering profile refresh...");
+ hasRefreshedProfile = true;
// Add a small delay to ensure relays are ready
setTimeout(() => {
refreshProfile();
@@ -131,12 +143,13 @@
$effect(() => {
const currentUser = userState;
- if (currentUser.signedIn && currentUser.npub && currentUser.loginMethod) {
+ if (currentUser.signedIn && currentUser.npub && currentUser.loginMethod && !isRefreshingProfile) {
console.log("Profile: Login method detected:", currentUser.loginMethod);
// If switching to read-only mode (npub), refresh profile
- if (currentUser.loginMethod === "npub" && !isRefreshingProfile) {
+ if (currentUser.loginMethod === "npub" && !hasRefreshedProfile) {
console.log("Profile: Switching to read-only mode, refreshing profile...");
+ hasRefreshedProfile = true;
setTimeout(() => {
refreshProfile();
}, 500);
@@ -150,12 +163,13 @@
$effect(() => {
const currentUser = userState;
- if (currentUser.signedIn && currentUser.loginMethod !== previousLoginMethod) {
+ if (currentUser.signedIn && currentUser.loginMethod !== previousLoginMethod && !isRefreshingProfile) {
console.log("Profile: Login method changed from", previousLoginMethod, "to", currentUser.loginMethod);
// If switching from Amber to npub (read-only), refresh profile
- if (previousLoginMethod === "amber" && currentUser.loginMethod === "npub") {
+ if (previousLoginMethod === "amber" && currentUser.loginMethod === "npub" && !hasRefreshedProfile) {
console.log("Profile: Switching from Amber to read-only mode, refreshing profile...");
+ hasRefreshedProfile = true;
setTimeout(() => {
refreshProfile();
}, 1000);
diff --git a/src/lib/ndk.ts b/src/lib/ndk.ts
index 6e1120e..7b9745e 100644
--- a/src/lib/ndk.ts
+++ b/src/lib/ndk.ts
@@ -386,10 +386,13 @@ function createRelayWithAuth(url: string, ndk: NDK): NDKRelay {
*/
export async function getActiveRelaySet(ndk: NDK): Promise<{ inboxRelays: string[]; outboxRelays: string[] }> {
const user = get(userStore);
+ console.debug('[NDK.ts] getActiveRelaySet: User state:', { signedIn: user.signedIn, hasNdkUser: !!user.ndkUser, pubkey: user.pubkey });
if (user.signedIn && user.ndkUser) {
+ console.debug('[NDK.ts] getActiveRelaySet: Building relay set for authenticated user:', user.ndkUser.pubkey);
return await buildCompleteRelaySet(ndk, user.ndkUser);
} else {
+ console.debug('[NDK.ts] getActiveRelaySet: Building relay set for anonymous user');
return await buildCompleteRelaySet(ndk, null);
}
}
@@ -400,25 +403,33 @@ export async function getActiveRelaySet(ndk: NDK): Promise<{ inboxRelays: string
*/
export async function updateActiveRelayStores(ndk: NDK): Promise {
try {
+ console.debug('[NDK.ts] updateActiveRelayStores: Starting relay store update');
+
// Get the active relay set from the relay management system
const relaySet = await getActiveRelaySet(ndk);
+ console.debug('[NDK.ts] updateActiveRelayStores: Got relay set:', relaySet);
// Update the stores with the new relay configuration
activeInboxRelays.set(relaySet.inboxRelays);
activeOutboxRelays.set(relaySet.outboxRelays);
+ console.debug('[NDK.ts] updateActiveRelayStores: Updated stores with inbox:', relaySet.inboxRelays.length, 'outbox:', relaySet.outboxRelays.length);
// Add relays to NDK pool (deduplicated)
const allRelayUrls = deduplicateRelayUrls([...relaySet.inboxRelays, ...relaySet.outboxRelays]);
+ console.debug('[NDK.ts] updateActiveRelayStores: Adding', allRelayUrls.length, 'relays to NDK pool');
+
for (const url of allRelayUrls) {
try {
const relay = createRelayWithAuth(url, ndk);
ndk.pool?.addRelay(relay);
} catch (error) {
- // Silently ignore relay addition failures
+ console.debug('[NDK.ts] updateActiveRelayStores: Failed to add relay', url, ':', error);
}
}
+
+ console.debug('[NDK.ts] updateActiveRelayStores: Relay store update completed');
} catch (error) {
- // Silently ignore relay store update errors
+ console.warn('[NDK.ts] updateActiveRelayStores: Error updating relay stores:', error);
}
}
diff --git a/src/lib/stores/userStore.ts b/src/lib/stores/userStore.ts
index 4e2b067..376367c 100644
--- a/src/lib/stores/userStore.ts
+++ b/src/lib/stores/userStore.ts
@@ -8,9 +8,10 @@ import {
NDKRelay,
} from "@nostr-dev-kit/ndk";
import { getUserMetadata } from "$lib/utils/nostrUtils";
-import { ndkInstance, activeInboxRelays, activeOutboxRelays } from "$lib/ndk";
+import { ndkInstance, activeInboxRelays, activeOutboxRelays, updateActiveRelayStores } from "$lib/ndk";
import { loginStorageKey } from "$lib/consts";
import { nip19 } from "nostr-tools";
+import { userPubkey } from "$lib/stores/authStore.Svelte";
export interface UserState {
pubkey: string | null;
@@ -209,6 +210,15 @@ export async function loginWithExtension() {
console.log("Login with extension - setting userStore with:", userState);
userStore.set(userState);
+
+ // Update relay stores with the new user's relays
+ try {
+ console.debug('[userStore.ts] loginWithExtension: Updating relay stores for authenticated user');
+ await updateActiveRelayStores(ndk);
+ } catch (error) {
+ console.warn('[userStore.ts] loginWithExtension: Failed to update relay stores:', error);
+ }
+
clearLogin();
localStorage.removeItem("alexandria/logout/flag");
persistLogin(user, "extension");
@@ -266,6 +276,16 @@ export async function loginWithAmber(amberSigner: NDKSigner, user: NDKUser) {
console.log("Login with Amber - setting userStore with:", userState);
userStore.set(userState);
+ userPubkey.set(user.pubkey);
+
+ // Update relay stores with the new user's relays
+ try {
+ console.debug('[userStore.ts] loginWithAmber: Updating relay stores for authenticated user');
+ await updateActiveRelayStores(ndk);
+ } catch (error) {
+ console.warn('[userStore.ts] loginWithAmber: Failed to update relay stores:', error);
+ }
+
clearLogin();
localStorage.removeItem("alexandria/logout/flag");
persistLogin(user, "amber");
@@ -330,6 +350,16 @@ export async function loginWithNpub(pubkeyOrNpub: string) {
console.log("Login with npub - setting userStore with:", userState);
userStore.set(userState);
+ userPubkey.set(user.pubkey);
+
+ // Update relay stores with the new user's relays
+ try {
+ console.debug('[userStore.ts] loginWithNpub: Updating relay stores for authenticated user');
+ await updateActiveRelayStores(ndk);
+ } catch (error) {
+ console.warn('[userStore.ts] loginWithNpub: Failed to update relay stores:', error);
+ }
+
clearLogin();
localStorage.removeItem("alexandria/logout/flag");
persistLogin(user, "npub");
@@ -393,6 +423,7 @@ export function logoutUser() {
signer: null,
signedIn: false,
});
+ userPubkey.set(null);
const ndk = get(ndkInstance);
if (ndk) {
diff --git a/src/lib/utils/nostrUtils.ts b/src/lib/utils/nostrUtils.ts
index f70d139..da8bb1b 100644
--- a/src/lib/utils/nostrUtils.ts
+++ b/src/lib/utils/nostrUtils.ts
@@ -5,7 +5,7 @@ import { npubCache } from "./npubCache";
import NDK, { NDKEvent, NDKRelaySet, NDKUser } from "@nostr-dev-kit/ndk";
import type { NDKFilter, NDKKind } from "@nostr-dev-kit/ndk";
import { communityRelays, secondaryRelays, anonymousRelays } from "$lib/consts";
-import { activeInboxRelays } from "$lib/ndk";
+import { activeInboxRelays, activeOutboxRelays } from "$lib/ndk";
import { NDKRelaySet as NDKRelaySetFromNDK } from "@nostr-dev-kit/ndk";
import { sha256 } from "@noble/hashes/sha256";
import { schnorr } from "@noble/curves/secp256k1";
@@ -439,19 +439,22 @@ export async function fetchEventWithFallback(
filterOrId: string | NDKFilter,
timeoutMs: number = 3000,
): Promise {
- // Use the active inbox relays from the relay management system
+ // Use both inbox and outbox relays for better event discovery
const inboxRelays = get(activeInboxRelays);
+ const outboxRelays = get(activeOutboxRelays);
+ const allRelays = [...(inboxRelays || []), ...(outboxRelays || [])];
console.log("fetchEventWithFallback: Using inbox relays:", inboxRelays);
+ console.log("fetchEventWithFallback: Using outbox relays:", outboxRelays);
// Check if we have any relays available
- if (inboxRelays.length === 0) {
- console.warn("fetchEventWithFallback: No inbox relays available for event fetch");
+ if (allRelays.length === 0) {
+ console.warn("fetchEventWithFallback: No relays available for event fetch");
return null;
}
- // Create relay set from active inbox relays
- const relaySet = NDKRelaySetFromNDK.fromRelayUrls(inboxRelays, ndk);
+ // Create relay set from all available relays
+ const relaySet = NDKRelaySetFromNDK.fromRelayUrls(allRelays, ndk);
try {
if (relaySet.relays.size === 0) {
diff --git a/src/lib/utils/relay_management.ts b/src/lib/utils/relay_management.ts
index 09aa5ac..2787938 100644
--- a/src/lib/utils/relay_management.ts
+++ b/src/lib/utils/relay_management.ts
@@ -287,6 +287,7 @@ export async function getUserBlockedRelays(ndk: NDK, user: NDKUser): Promise {
try {
+ console.debug('[relay_management.ts] Fetching outbox relays for user:', user.pubkey);
const relayList = await ndk.fetchEvent(
{
kinds: [10002],
@@ -300,16 +301,29 @@ export async function getUserOutboxRelays(ndk: NDK, user: NDKUser): Promise {
+ console.debug('[relay_management.ts] Processing tag:', tag);
if (tag[0] === 'w' && tag[1]) {
outboxRelays.push(tag[1]);
+ console.debug('[relay_management.ts] Added outbox relay:', tag[1]);
+ } else if (tag[0] === 'r' && tag[1]) {
+ // Some relay lists use 'r' for both inbox and outbox
+ outboxRelays.push(tag[1]);
+ console.debug('[relay_management.ts] Added relay (r tag):', tag[1]);
+ } else {
+ console.debug('[relay_management.ts] Skipping tag:', tag[0], 'value:', tag[1]);
}
});
+ console.debug('[relay_management.ts] Final outbox relays:', outboxRelays);
return outboxRelays;
} catch (error) {
console.info('[relay_management.ts] Error fetching user outbox relays:', error);
@@ -317,6 +331,56 @@ export async function getUserOutboxRelays(ndk: NDK, user: NDKUser): Promise {
+ try {
+ // Check if we're in a browser environment with extension support
+ if (typeof window === 'undefined' || !window.nostr) {
+ console.debug('[relay_management.ts] No window.nostr available');
+ return [];
+ }
+
+ console.debug('[relay_management.ts] Extension available, checking for getRelays()');
+ const extensionRelays: string[] = [];
+
+ // Try to get relays from the extension's API
+ // Different extensions may expose their relay config differently
+ if (window.nostr.getRelays) {
+ console.debug('[relay_management.ts] getRelays() method found, calling it...');
+ try {
+ const relays = await window.nostr.getRelays();
+ console.debug('[relay_management.ts] getRelays() returned:', relays);
+ if (relays && typeof relays === 'object') {
+ // Convert relay object to array of URLs
+ const relayUrls = Object.keys(relays);
+ extensionRelays.push(...relayUrls);
+ console.debug('[relay_management.ts] Got relays from extension:', relayUrls);
+ }
+ } catch (error) {
+ console.debug('[relay_management.ts] Extension getRelays() failed:', error);
+ }
+ } else {
+ console.debug('[relay_management.ts] getRelays() method not found on window.nostr');
+ }
+
+ // If getRelays() didn't work, try alternative methods
+ if (extensionRelays.length === 0) {
+ // Some extensions might expose relays through other methods
+ // This is a fallback for extensions that don't expose getRelays()
+ console.debug('[relay_management.ts] Extension does not expose relay configuration');
+ }
+
+ console.debug('[relay_management.ts] Final extension relays:', extensionRelays);
+ return extensionRelays;
+ } catch (error) {
+ console.debug('[relay_management.ts] Error getting extension relays:', error);
+ return [];
+ }
+}
+
/**
* Tests a set of relays in batches to avoid overwhelming them
* @param relayUrls Array of relay URLs to test
@@ -361,37 +425,55 @@ export async function buildCompleteRelaySet(
ndk: NDK,
user: NDKUser | null
): Promise<{ inboxRelays: string[]; outboxRelays: string[] }> {
+ console.debug('[relay_management.ts] buildCompleteRelaySet: Starting with user:', user?.pubkey || 'null');
+
// Discover local relays first
const discoveredLocalRelays = await discoverLocalRelays(ndk);
+ console.debug('[relay_management.ts] buildCompleteRelaySet: Discovered local relays:', discoveredLocalRelays);
// Get user-specific relays if available
let userOutboxRelays: string[] = [];
let userLocalRelays: string[] = [];
let blockedRelays: string[] = [];
+ let extensionRelays: string[] = [];
if (user) {
+ console.debug('[relay_management.ts] buildCompleteRelaySet: Fetching user-specific relays for:', user.pubkey);
+
try {
userOutboxRelays = await getUserOutboxRelays(ndk, user);
+ console.debug('[relay_management.ts] buildCompleteRelaySet: User outbox relays:', userOutboxRelays);
} catch (error) {
console.debug('[relay_management.ts] Error fetching user outbox relays:', error);
}
try {
userLocalRelays = await getUserLocalRelays(ndk, user);
+ console.debug('[relay_management.ts] buildCompleteRelaySet: User local relays:', userLocalRelays);
} catch (error) {
console.debug('[relay_management.ts] Error fetching user local relays:', error);
}
try {
blockedRelays = await getUserBlockedRelays(ndk, user);
+ console.debug('[relay_management.ts] buildCompleteRelaySet: User blocked relays:', blockedRelays);
} catch (error) {
// Silently ignore blocked relay fetch errors
}
+
+ try {
+ extensionRelays = await getExtensionRelays();
+ console.debug('[relay_management.ts] Extension relays gathered:', extensionRelays);
+ } catch (error) {
+ console.debug('[relay_management.ts] Error fetching extension relays:', error);
+ }
+ } else {
+ console.debug('[relay_management.ts] buildCompleteRelaySet: No user provided, skipping user-specific relays');
}
// Build initial relay sets and deduplicate
const finalInboxRelays = deduplicateRelayUrls([...discoveredLocalRelays, ...userLocalRelays]);
- const finalOutboxRelays = deduplicateRelayUrls([...discoveredLocalRelays, ...userOutboxRelays]);
+ const finalOutboxRelays = deduplicateRelayUrls([...discoveredLocalRelays, ...userOutboxRelays, ...extensionRelays]);
// Test relays and filter out non-working ones
let testedInboxRelays: string[] = [];
@@ -441,5 +523,9 @@ export async function buildCompleteRelaySet(
};
}
+ console.debug('[relay_management.ts] buildCompleteRelaySet: Final relay sets - inbox:', finalRelaySet.inboxRelays.length, 'outbox:', finalRelaySet.outboxRelays.length);
+ console.debug('[relay_management.ts] buildCompleteRelaySet: Final inbox relays:', finalRelaySet.inboxRelays);
+ console.debug('[relay_management.ts] buildCompleteRelaySet: Final outbox relays:', finalRelaySet.outboxRelays);
+
return finalRelaySet;
}
\ No newline at end of file