|
|
import NDK, { |
|
|
NDKNip07Signer, |
|
|
NDKRelay, |
|
|
NDKRelayAuthPolicies, |
|
|
NDKRelaySet, |
|
|
NDKUser, |
|
|
NDKEvent, |
|
|
} from "@nostr-dev-kit/ndk"; |
|
|
import { get, writable, type Writable } from "svelte/store"; |
|
|
import { |
|
|
secondaryRelays, |
|
|
FeedType, |
|
|
loginStorageKey, |
|
|
communityRelays, |
|
|
anonymousRelays, |
|
|
searchRelays, |
|
|
} from "./consts"; |
|
|
import { |
|
|
buildCompleteRelaySet, |
|
|
testRelayConnection, |
|
|
discoverLocalRelays, |
|
|
getUserLocalRelays, |
|
|
getUserBlockedRelays, |
|
|
getUserOutboxRelays, |
|
|
deduplicateRelayUrls, |
|
|
} from "./utils/relay_management"; |
|
|
|
|
|
// Re-export testRelayConnection for components that need it |
|
|
export { testRelayConnection }; |
|
|
import { startNetworkMonitoring, NetworkCondition } from "./utils/network_detection"; |
|
|
import { userStore } from "./stores/userStore"; |
|
|
import { userPubkey } from "$lib/stores/authStore.Svelte"; |
|
|
import { startNetworkStatusMonitoring, stopNetworkStatusMonitoring } from "./stores/networkStore"; |
|
|
|
|
|
export const ndkInstance: Writable<NDK> = writable(); |
|
|
export const ndkSignedIn = writable(false); |
|
|
export const activePubkey = writable<string | null>(null); |
|
|
export const inboxRelays = writable<string[]>([]); |
|
|
export const outboxRelays = writable<string[]>([]); |
|
|
|
|
|
// New relay management stores |
|
|
export const activeInboxRelays = writable<string[]>([]); |
|
|
export const activeOutboxRelays = writable<string[]>([]); |
|
|
|
|
|
/** |
|
|
* Custom authentication policy that handles NIP-42 authentication manually |
|
|
* when the default NDK authentication fails |
|
|
*/ |
|
|
class CustomRelayAuthPolicy { |
|
|
private ndk: NDK; |
|
|
private challenges: Map<string, string> = new Map(); |
|
|
|
|
|
constructor(ndk: NDK) { |
|
|
this.ndk = ndk; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Handles authentication for a relay |
|
|
* @param relay The relay to authenticate with |
|
|
* @returns Promise that resolves when authentication is complete |
|
|
*/ |
|
|
async authenticate(relay: NDKRelay): Promise<void> { |
|
|
if (!this.ndk.signer || !this.ndk.activeUser) { |
|
|
console.warn( |
|
|
"[NDK.ts] No signer or active user available for relay authentication", |
|
|
); |
|
|
return; |
|
|
} |
|
|
|
|
|
try { |
|
|
console.debug(`[NDK.ts] Setting up authentication for ${relay.url}`); |
|
|
|
|
|
// Listen for AUTH challenges |
|
|
relay.on("auth", (challenge: string) => { |
|
|
console.debug( |
|
|
`[NDK.ts] Received AUTH challenge from ${relay.url}:`, |
|
|
challenge, |
|
|
); |
|
|
this.challenges.set(relay.url, challenge); |
|
|
this.handleAuthChallenge(relay, challenge); |
|
|
}); |
|
|
|
|
|
// Listen for auth-required errors (handle via notice events) |
|
|
relay.on("notice", (message: string) => { |
|
|
if (message.includes("auth-required")) { |
|
|
console.debug(`[NDK.ts] Auth required from ${relay.url}:`, message); |
|
|
this.handleAuthRequired(relay, message); |
|
|
} |
|
|
}); |
|
|
|
|
|
// Listen for successful authentication |
|
|
relay.on("authed", () => { |
|
|
console.debug(`[NDK.ts] Successfully authenticated to ${relay.url}`); |
|
|
}); |
|
|
|
|
|
// Listen for authentication failures |
|
|
relay.on("auth:failed", (error: any) => { |
|
|
console.error( |
|
|
`[NDK.ts] Authentication failed for ${relay.url}:`, |
|
|
error, |
|
|
); |
|
|
}); |
|
|
} catch (error) { |
|
|
console.error( |
|
|
`[NDK.ts] Error setting up authentication for ${relay.url}:`, |
|
|
error, |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* Handles AUTH challenge from relay |
|
|
*/ |
|
|
private async handleAuthChallenge( |
|
|
relay: NDKRelay, |
|
|
challenge: string, |
|
|
): Promise<void> { |
|
|
try { |
|
|
if (!this.ndk.signer || !this.ndk.activeUser) { |
|
|
console.warn("[NDK.ts] No signer available for AUTH challenge"); |
|
|
return; |
|
|
} |
|
|
|
|
|
// Create NIP-42 authentication event |
|
|
const authEvent = { |
|
|
kind: 22242, |
|
|
created_at: Math.floor(Date.now() / 1000), |
|
|
tags: [ |
|
|
["relay", relay.url], |
|
|
["challenge", challenge], |
|
|
], |
|
|
content: "", |
|
|
pubkey: this.ndk.activeUser.pubkey, |
|
|
}; |
|
|
|
|
|
// Create and sign the authentication event using NDKEvent |
|
|
const authNDKEvent = new NDKEvent(this.ndk, authEvent); |
|
|
await authNDKEvent.sign(); |
|
|
|
|
|
// Send AUTH message to relay using the relay's publish method |
|
|
await relay.publish(authNDKEvent); |
|
|
console.debug(`[NDK.ts] Sent AUTH to ${relay.url}`); |
|
|
} catch (error) { |
|
|
console.error( |
|
|
`[NDK.ts] Error handling AUTH challenge for ${relay.url}:`, |
|
|
error, |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* Handles auth-required error from relay |
|
|
*/ |
|
|
private async handleAuthRequired( |
|
|
relay: NDKRelay, |
|
|
message: string, |
|
|
): Promise<void> { |
|
|
const challenge = this.challenges.get(relay.url); |
|
|
if (challenge) { |
|
|
await this.handleAuthChallenge(relay, challenge); |
|
|
} else { |
|
|
console.warn( |
|
|
`[NDK.ts] Auth required from ${relay.url} but no challenge available`, |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* Checks if the current environment might cause WebSocket protocol downgrade |
|
|
*/ |
|
|
export function checkEnvironmentForWebSocketDowngrade(): void { |
|
|
console.debug("[NDK.ts] Environment Check for WebSocket Protocol:"); |
|
|
|
|
|
const isLocalhost = |
|
|
window.location.hostname === "localhost" || |
|
|
window.location.hostname === "127.0.0.1"; |
|
|
const isHttp = window.location.protocol === "http:"; |
|
|
const isHttps = window.location.protocol === "https:"; |
|
|
|
|
|
console.debug("[NDK.ts] - Is localhost:", isLocalhost); |
|
|
console.debug("[NDK.ts] - Protocol:", window.location.protocol); |
|
|
console.debug("[NDK.ts] - Is HTTP:", isHttp); |
|
|
console.debug("[NDK.ts] - Is HTTPS:", isHttps); |
|
|
|
|
|
if (isLocalhost && isHttp) { |
|
|
console.warn( |
|
|
"[NDK.ts] ⚠️ Running on localhost with HTTP - WebSocket downgrade to ws:// is expected", |
|
|
); |
|
|
console.warn("[NDK.ts] This is normal for development environments"); |
|
|
} else if (isHttp) { |
|
|
console.error( |
|
|
"[NDK.ts] ❌ Running on HTTP - WebSocket connections will be insecure", |
|
|
); |
|
|
console.error("[NDK.ts] Consider using HTTPS in production"); |
|
|
} else if (isHttps) { |
|
|
console.debug( |
|
|
"[NDK.ts] ✓ Running on HTTPS - Secure WebSocket connections should work", |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* Checks WebSocket protocol support and logs diagnostic information |
|
|
*/ |
|
|
export function checkWebSocketSupport(): void { |
|
|
console.debug("[NDK.ts] WebSocket Support Diagnostics:"); |
|
|
console.debug("[NDK.ts] - Protocol:", window.location.protocol); |
|
|
console.debug("[NDK.ts] - Hostname:", window.location.hostname); |
|
|
console.debug("[NDK.ts] - Port:", window.location.port); |
|
|
console.debug("[NDK.ts] - User Agent:", navigator.userAgent); |
|
|
|
|
|
// Test if secure WebSocket is supported |
|
|
try { |
|
|
const testWs = new WebSocket("wss://echo.websocket.org"); |
|
|
testWs.onopen = () => { |
|
|
console.debug("[NDK.ts] ✓ Secure WebSocket (wss://) is supported"); |
|
|
testWs.close(); |
|
|
}; |
|
|
testWs.onerror = () => { |
|
|
console.warn("[NDK.ts] ✗ Secure WebSocket (wss://) may not be supported"); |
|
|
}; |
|
|
} catch (error) { |
|
|
console.warn("[NDK.ts] ✗ WebSocket test failed:", error); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
* Gets the user's pubkey from local storage, if it exists. |
|
|
* @returns The user's pubkey, or null if there is no logged-in user. |
|
|
* @remarks Local storage is used in place of cookies to persist the user's login across browser |
|
|
* sessions. |
|
|
*/ |
|
|
export function getPersistedLogin(): string | null { |
|
|
const pubkey = localStorage.getItem(loginStorageKey); |
|
|
return pubkey; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Writes the user's pubkey to local storage. |
|
|
* @param user The user to persist. |
|
|
* @remarks Use this function when the user logs in. Currently, only one pubkey is stored at a |
|
|
* time. |
|
|
*/ |
|
|
export function persistLogin(user: NDKUser): void { |
|
|
localStorage.setItem(loginStorageKey, user.pubkey); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Clears the user's pubkey from local storage. |
|
|
* @remarks Use this function when the user logs out. |
|
|
*/ |
|
|
export function clearLogin(): void { |
|
|
localStorage.removeItem(loginStorageKey); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Constructs a key use to designate a user's relay lists in local storage. |
|
|
* @param user The user for whom to construct the key. |
|
|
* @param type The type of relay list to designate. |
|
|
* @returns The constructed key. |
|
|
*/ |
|
|
function getRelayStorageKey(user: NDKUser, type: "inbox" | "outbox"): string { |
|
|
return `${loginStorageKey}/${user.pubkey}/${type}`; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Stores the user's relay lists in local storage. |
|
|
* @param user The user for whom to store the relay lists. |
|
|
* @param inboxes The user's inbox relays. |
|
|
* @param outboxes The user's outbox relays. |
|
|
*/ |
|
|
function persistRelays( |
|
|
user: NDKUser, |
|
|
inboxes: Set<NDKRelay>, |
|
|
outboxes: Set<NDKRelay>, |
|
|
): void { |
|
|
localStorage.setItem( |
|
|
getRelayStorageKey(user, "inbox"), |
|
|
JSON.stringify(Array.from(inboxes).map((relay) => relay.url)), |
|
|
); |
|
|
localStorage.setItem( |
|
|
getRelayStorageKey(user, "outbox"), |
|
|
JSON.stringify(Array.from(outboxes).map((relay) => relay.url)), |
|
|
); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Retrieves the user's relay lists from local storage. |
|
|
* @param user The user for whom to retrieve the relay lists. |
|
|
* @returns A tuple of relay sets of the form `[inboxRelays, outboxRelays]`. Either set may be |
|
|
* empty if no relay lists were stored for the user. |
|
|
*/ |
|
|
function getPersistedRelays(user: NDKUser): [Set<string>, Set<string>] { |
|
|
const inboxes = new Set<string>( |
|
|
JSON.parse(localStorage.getItem(getRelayStorageKey(user, "inbox")) ?? "[]"), |
|
|
); |
|
|
const outboxes = new Set<string>( |
|
|
JSON.parse( |
|
|
localStorage.getItem(getRelayStorageKey(user, "outbox")) ?? "[]", |
|
|
), |
|
|
); |
|
|
|
|
|
return [inboxes, outboxes]; |
|
|
} |
|
|
|
|
|
export function clearPersistedRelays(user: NDKUser): void { |
|
|
localStorage.removeItem(getRelayStorageKey(user, "inbox")); |
|
|
localStorage.removeItem(getRelayStorageKey(user, "outbox")); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Ensures a relay URL uses secure WebSocket protocol |
|
|
* @param url The relay URL to secure |
|
|
* @returns The URL with wss:// protocol |
|
|
*/ |
|
|
function ensureSecureWebSocket(url: string): string { |
|
|
// Replace ws:// with wss:// if present |
|
|
const secureUrl = url.replace(/^ws:\/\//, "wss://"); |
|
|
|
|
|
if (secureUrl !== url) { |
|
|
console.warn( |
|
|
`[NDK.ts] Protocol downgrade detected: ${url} -> ${secureUrl}`, |
|
|
); |
|
|
} |
|
|
|
|
|
return secureUrl; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Creates a relay with proper authentication handling |
|
|
*/ |
|
|
function createRelayWithAuth(url: string, ndk: NDK): NDKRelay { |
|
|
console.debug(`[NDK.ts] Creating relay with URL: ${url}`); |
|
|
|
|
|
// Ensure the URL is using wss:// protocol |
|
|
const secureUrl = ensureSecureWebSocket(url); |
|
|
|
|
|
// Add connection timeout and error handling |
|
|
const relay = new NDKRelay( |
|
|
secureUrl, |
|
|
NDKRelayAuthPolicies.signIn({ ndk }), |
|
|
ndk, |
|
|
); |
|
|
|
|
|
// Set up connection timeout |
|
|
const connectionTimeout = setTimeout(() => { |
|
|
console.warn(`[NDK.ts] Connection timeout for ${secureUrl}`); |
|
|
relay.disconnect(); |
|
|
}, 10000); // 10 second timeout |
|
|
|
|
|
// Set up custom authentication handling only if user is signed in |
|
|
if (ndk.signer && ndk.activeUser) { |
|
|
const authPolicy = new CustomRelayAuthPolicy(ndk); |
|
|
relay.on("connect", () => { |
|
|
console.debug(`[NDK.ts] Relay connected: ${secureUrl}`); |
|
|
clearTimeout(connectionTimeout); |
|
|
authPolicy.authenticate(relay); |
|
|
}); |
|
|
} else { |
|
|
relay.on("connect", () => { |
|
|
console.debug(`[NDK.ts] Relay connected: ${secureUrl}`); |
|
|
clearTimeout(connectionTimeout); |
|
|
}); |
|
|
} |
|
|
|
|
|
// Add error handling |
|
|
relay.on("disconnect", () => { |
|
|
console.debug(`[NDK.ts] Relay disconnected: ${secureUrl}`); |
|
|
clearTimeout(connectionTimeout); |
|
|
}); |
|
|
|
|
|
return relay; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
* Gets the active relay set for the current user |
|
|
* @param ndk NDK instance |
|
|
* @returns Promise that resolves to object with inbox and outbox relay arrays |
|
|
*/ |
|
|
export async function getActiveRelaySet(ndk: NDK): Promise<{ inboxRelays: string[]; outboxRelays: string[] }> { |
|
|
const user = get(userStore); |
|
|
|
|
|
if (user.signedIn && user.ndkUser) { |
|
|
return await buildCompleteRelaySet(ndk, user.ndkUser); |
|
|
} else { |
|
|
return await buildCompleteRelaySet(ndk, null); |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* Updates the active relay stores and NDK pool with new relay URLs |
|
|
* @param ndk NDK instance |
|
|
*/ |
|
|
export async function updateActiveRelayStores(ndk: NDK): Promise<void> { |
|
|
try { |
|
|
// Get the active relay set from the relay management system |
|
|
const relaySet = await getActiveRelaySet(ndk); |
|
|
|
|
|
// Update the stores with the new relay configuration |
|
|
activeInboxRelays.set(relaySet.inboxRelays); |
|
|
activeOutboxRelays.set(relaySet.outboxRelays); |
|
|
|
|
|
// Add relays to NDK pool (deduplicated) |
|
|
const allRelayUrls = deduplicateRelayUrls([...relaySet.inboxRelays, ...relaySet.outboxRelays]); |
|
|
for (const url of allRelayUrls) { |
|
|
try { |
|
|
const relay = createRelayWithAuth(url, ndk); |
|
|
ndk.pool?.addRelay(relay); |
|
|
} catch (error) { |
|
|
// Silently ignore relay addition failures |
|
|
} |
|
|
} |
|
|
} catch (error) { |
|
|
// Silently ignore relay store update errors |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* Logs the current relay configuration to console |
|
|
*/ |
|
|
export function logCurrentRelayConfiguration(): void { |
|
|
const inboxRelays = get(activeInboxRelays); |
|
|
const outboxRelays = get(activeOutboxRelays); |
|
|
|
|
|
console.log('🔌 Current Relay Configuration:'); |
|
|
console.log('📥 Inbox Relays:', inboxRelays); |
|
|
console.log('📤 Outbox Relays:', outboxRelays); |
|
|
console.log(`📊 Total: ${inboxRelays.length} inbox, ${outboxRelays.length} outbox`); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Updates relay stores when user state changes |
|
|
* @param ndk NDK instance |
|
|
*/ |
|
|
export async function refreshRelayStores(ndk: NDK): Promise<void> { |
|
|
console.debug('[NDK.ts] Refreshing relay stores due to user state change'); |
|
|
await updateActiveRelayStores(ndk); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Updates relay stores when network condition changes |
|
|
* @param ndk NDK instance |
|
|
*/ |
|
|
export async function refreshRelayStoresOnNetworkChange(ndk: NDK): Promise<void> { |
|
|
console.debug('[NDK.ts] Refreshing relay stores due to network condition change'); |
|
|
await updateActiveRelayStores(ndk); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Starts network monitoring for relay optimization |
|
|
* @param ndk NDK instance |
|
|
*/ |
|
|
export function startNetworkMonitoringForRelays(ndk: NDK): void { |
|
|
// Use centralized network monitoring instead of separate monitoring |
|
|
startNetworkStatusMonitoring(); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Creates NDKRelaySet from relay URLs with proper authentication |
|
|
* @param relayUrls Array of relay URLs |
|
|
* @param ndk NDK instance |
|
|
* @returns NDKRelaySet |
|
|
*/ |
|
|
function createRelaySetFromUrls(relayUrls: string[], ndk: NDK): NDKRelaySet { |
|
|
const relays = relayUrls.map(url => |
|
|
new NDKRelay(url, NDKRelayAuthPolicies.signIn({ ndk }), ndk) |
|
|
); |
|
|
|
|
|
return new NDKRelaySet(new Set(relays), ndk); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Gets the active relay set as NDKRelaySet for use in queries |
|
|
* @param ndk NDK instance |
|
|
* @param useInbox Whether to use inbox relays (true) or outbox relays (false) |
|
|
* @returns Promise that resolves to NDKRelaySet |
|
|
*/ |
|
|
export async function getActiveRelaySetAsNDKRelaySet( |
|
|
ndk: NDK, |
|
|
useInbox: boolean = true |
|
|
): Promise<NDKRelaySet> { |
|
|
const relaySet = await getActiveRelaySet(ndk); |
|
|
const urls = useInbox ? relaySet.inboxRelays : relaySet.outboxRelays; |
|
|
|
|
|
return createRelaySetFromUrls(urls, ndk); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Initializes an instance of NDK with the new relay management system |
|
|
* @returns The initialized NDK instance |
|
|
*/ |
|
|
export function initNdk(): NDK { |
|
|
console.debug("[NDK.ts] Initializing NDK with new relay management system"); |
|
|
|
|
|
const ndk = new NDK({ |
|
|
autoConnectUserRelays: false, // We'll manage relays manually |
|
|
enableOutboxModel: true, |
|
|
}); |
|
|
|
|
|
// Set up custom authentication policy |
|
|
ndk.relayAuthDefaultPolicy = NDKRelayAuthPolicies.signIn({ ndk }); |
|
|
|
|
|
// Connect with better error handling and reduced retry attempts |
|
|
let retryCount = 0; |
|
|
const maxRetries = 2; |
|
|
|
|
|
const attemptConnection = async () => { |
|
|
try { |
|
|
await ndk.connect(); |
|
|
console.debug("[NDK.ts] NDK connected successfully"); |
|
|
// Update relay stores after connection |
|
|
await updateActiveRelayStores(ndk); |
|
|
// Start network monitoring for relay optimization |
|
|
startNetworkMonitoringForRelays(ndk); |
|
|
} catch (error) { |
|
|
console.warn("[NDK.ts] Failed to connect NDK:", error); |
|
|
|
|
|
// Only retry a limited number of times |
|
|
if (retryCount < maxRetries) { |
|
|
retryCount++; |
|
|
console.debug(`[NDK.ts] Attempting to reconnect (${retryCount}/${maxRetries})...`); |
|
|
setTimeout(attemptConnection, 3000); |
|
|
} else { |
|
|
console.warn("[NDK.ts] Max retries reached, continuing with limited functionality"); |
|
|
// Still try to update relay stores even if connection failed |
|
|
try { |
|
|
await updateActiveRelayStores(ndk); |
|
|
startNetworkMonitoringForRelays(ndk); |
|
|
} catch (storeError) { |
|
|
console.warn("[NDK.ts] Failed to update relay stores:", storeError); |
|
|
} |
|
|
} |
|
|
} |
|
|
}; |
|
|
|
|
|
attemptConnection(); |
|
|
|
|
|
return ndk; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Signs in with a NIP-07 browser extension using the new relay management system |
|
|
* @returns The user's profile, if it is available |
|
|
* @throws If sign-in fails |
|
|
*/ |
|
|
export async function loginWithExtension( |
|
|
pubkey?: string, |
|
|
): Promise<NDKUser | null> { |
|
|
try { |
|
|
const ndk = get(ndkInstance); |
|
|
const signer = new NDKNip07Signer(); |
|
|
const signerUser = await signer.user(); |
|
|
|
|
|
// TODO: Handle changing pubkeys. |
|
|
if (pubkey && signerUser.pubkey !== pubkey) { |
|
|
console.debug("[NDK.ts] Switching pubkeys from last login."); |
|
|
} |
|
|
|
|
|
activePubkey.set(signerUser.pubkey); |
|
|
userPubkey.set(signerUser.pubkey); |
|
|
|
|
|
const user = ndk.getUser({ pubkey: signerUser.pubkey }); |
|
|
|
|
|
// Update relay stores with the new system |
|
|
await updateActiveRelayStores(ndk); |
|
|
|
|
|
ndk.signer = signer; |
|
|
ndk.activeUser = user; |
|
|
|
|
|
ndkInstance.set(ndk); |
|
|
ndkSignedIn.set(true); |
|
|
|
|
|
return user; |
|
|
} catch (e) { |
|
|
throw new Error(`Failed to sign in with NIP-07 extension: ${e}`); |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* Handles logging out a user. |
|
|
* @param user The user to log out. |
|
|
*/ |
|
|
export function logout(user: NDKUser): void { |
|
|
clearLogin(); |
|
|
clearPersistedRelays(user); |
|
|
activePubkey.set(null); |
|
|
userPubkey.set(null); |
|
|
ndkSignedIn.set(false); |
|
|
|
|
|
// Clear relay stores |
|
|
activeInboxRelays.set([]); |
|
|
activeOutboxRelays.set([]); |
|
|
|
|
|
// Stop network monitoring |
|
|
stopNetworkStatusMonitoring(); |
|
|
|
|
|
// Re-initialize with anonymous instance |
|
|
const newNdk = initNdk(); |
|
|
ndkInstance.set(newNdk); |
|
|
} |
|
|
|
|
|
|
|
|
|