Browse Source

login working

master
silberengel 8 months ago
parent
commit
fd3b26741d
  1. 172
      src/lib/components/util/Profile.svelte
  2. 73
      src/lib/stores/userStore.ts
  3. 35
      src/lib/utils/nostrUtils.ts

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

@ -20,6 +20,8 @@ @@ -20,6 +20,8 @@
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 } from "$lib/ndk";
let { pubkey, isNav = false } = $props<{ pubkey?: string, isNav?: boolean }>();
@ -35,7 +37,7 @@ @@ -35,7 +37,7 @@
let profileAvatarId = "profile-avatar-btn";
let showAmberFallback = $state(false);
let fallbackCheckInterval: ReturnType<typeof setInterval> | null = null;
let isProfileLoading = $state(false);
let isRefreshingProfile = $state(false);
onMount(() => {
if (localStorage.getItem("alexandria/amber/fallback") === "1") {
@ -44,7 +46,7 @@ @@ -44,7 +46,7 @@
}
});
// Use profile data from userStore instead of fetching separately
// Use profile data from userStore
let userState = $derived($userStore);
let profile = $derived(userState.profile);
let pfp = $derived(profile?.picture);
@ -52,6 +54,14 @@ @@ -52,6 +54,14 @@
let tag = $derived(profile?.name);
let npub = $derived(userState.npub);
// Debug logging
$effect(() => {
console.log("Profile component - userState:", userState);
console.log("Profile component - profile:", profile);
console.log("Profile component - pfp:", pfp);
console.log("Profile component - username:", username);
});
// Handle user state changes with effects
$effect(() => {
const currentUser = userState;
@ -87,51 +97,131 @@ @@ -87,51 +97,131 @@
}
});
// Fetch profile when user signs in or when pubkey changes
// Auto-refresh profile when user signs in
$effect(() => {
const currentUser = userState;
// If user is signed in but profile is not available, fetch it
if (currentUser.signedIn && !profile && currentUser.npub) {
const ndk = get(ndkInstance);
if (!ndk) return;
// If user is signed in and we have an npub but no profile data, refresh it
if (currentUser.signedIn && currentUser.npub && !profile?.name && !isRefreshingProfile) {
console.log("Profile: User signed in but no profile data, refreshing...");
refreshProfile();
}
});
isProfileLoading = true;
// Debug activeInboxRelays
$effect(() => {
const inboxRelays = get(activeInboxRelays);
console.log("Profile component - activeInboxRelays:", inboxRelays);
});
// Manual trigger to refresh profile when user signs in
$effect(() => {
const currentUser = userState;
if (currentUser.signedIn && currentUser.npub && !isRefreshingProfile) {
console.log("Profile: User signed in, triggering profile refresh...");
// Add a small delay to ensure relays are ready
setTimeout(() => {
refreshProfile();
}, 1000);
}
});
// Refresh profile when login method changes (e.g., Amber to read-only)
$effect(() => {
const currentUser = userState;
if (currentUser.signedIn && currentUser.npub && currentUser.loginMethod) {
console.log("Profile: Login method detected:", currentUser.loginMethod);
// Use the current user's npub to fetch profile
const user = ndk.getUser({ npub: currentUser.npub });
// If switching to read-only mode (npub), refresh profile
if (currentUser.loginMethod === "npub" && !isRefreshingProfile) {
console.log("Profile: Switching to read-only mode, refreshing profile...");
setTimeout(() => {
refreshProfile();
}, 500);
}
}
});
// Track login method changes and refresh profile when switching from Amber to npub
let previousLoginMethod = $state<string | null>(null);
$effect(() => {
const currentUser = userState;
if (currentUser.signedIn && currentUser.loginMethod !== previousLoginMethod) {
console.log("Profile: Login method changed from", previousLoginMethod, "to", currentUser.loginMethod);
user.fetchProfile().then((userProfile: NDKUserProfile | null) => {
if (userProfile && !profile) {
// Only update if we don't already have profile data
profile = userProfile;
}
isProfileLoading = false;
}).catch(() => {
isProfileLoading = false;
});
// If switching from Amber to npub (read-only), refresh profile
if (previousLoginMethod === "amber" && currentUser.loginMethod === "npub") {
console.log("Profile: Switching from Amber to read-only mode, refreshing profile...");
setTimeout(() => {
refreshProfile();
}, 1000);
}
previousLoginMethod = currentUser.loginMethod;
}
});
// Function to refresh profile data
async function refreshProfile() {
if (!userState.signedIn || !userState.npub) return;
// Fallback to fetching profile if not available in userStore and pubkey prop is provided
if (!profile && pubkey) {
isRefreshingProfile = true;
try {
console.log("Refreshing profile for npub:", userState.npub);
// Try using NDK's built-in profile fetching first
const ndk = get(ndkInstance);
if (!ndk) return;
isProfileLoading = true;
if (ndk && userState.ndkUser) {
console.log("Using NDK's built-in profile fetching");
const userProfile = await userState.ndkUser.fetchProfile();
console.log("NDK profile fetch result:", userProfile);
if (userProfile) {
const profileData = {
name: userProfile.name,
displayName: userProfile.displayName,
nip05: userProfile.nip05,
picture: userProfile.image,
about: userProfile.bio,
banner: userProfile.banner,
website: userProfile.website,
lud16: userProfile.lud16,
};
console.log("Converted profile data:", profileData);
// Update the userStore with fresh profile data
userStore.update(currentState => ({
...currentState,
profile: profileData
}));
return;
}
}
const user = ndk.getUser({ pubkey: pubkey ?? undefined });
// Fallback to getUserMetadata
console.log("Falling back to getUserMetadata");
const freshProfile = await getUserMetadata(userState.npub, true); // Force fresh fetch
console.log("Fresh profile data from getUserMetadata:", freshProfile);
user.fetchProfile().then((userProfile: NDKUserProfile | null) => {
if (userProfile && !profile) {
// Only update if we don't already have profile data
profile = userProfile;
}
isProfileLoading = false;
}).catch(() => {
isProfileLoading = false;
});
// Update the userStore with fresh profile data
userStore.update(currentState => ({
...currentState,
profile: freshProfile
}));
} catch (error) {
console.error("Failed to refresh profile:", error);
} finally {
isRefreshingProfile = false;
}
});
}
// Generate QR code
const generateQrCode = async (text: string): Promise<string> => {
@ -237,7 +327,6 @@ @@ -237,7 +327,6 @@
localStorage.removeItem("amber/nsec");
localStorage.removeItem("alexandria/amber/fallback");
logoutUser();
profile = null;
}
function handleViewProfile() {
@ -255,6 +344,12 @@ @@ -255,6 +344,12 @@
function handleAmberFallbackDismiss() {
showAmberFallback = false;
localStorage.removeItem("alexandria/amber/fallback");
// Refresh profile when switching to read-only mode
setTimeout(() => {
console.log("Profile: Amber fallback dismissed, refreshing profile for read-only mode...");
refreshProfile();
}, 500);
}
function shortenNpub(long: string | null | undefined) {
@ -337,7 +432,7 @@ @@ -337,7 +432,7 @@
type="button"
aria-label="Open profile menu"
>
{#if isProfileLoading && !pfp}
{#if !pfp}
<div class="h-6 w-6 rounded-full bg-gray-300 animate-pulse cursor-pointer"></div>
{:else}
<Avatar
@ -359,7 +454,7 @@ @@ -359,7 +454,7 @@
{#if username}
<h3 class="text-lg font-bold">{username}</h3>
{#if isNav}<h4 class="text-base">@{tag}</h4>{/if}
{:else if isProfileLoading}
{:else if !pfp}
<h3 class="text-lg font-bold">Loading profile...</h3>
{:else}
<h3 class="text-lg font-bold">Loading...</h3>
@ -381,6 +476,7 @@ @@ -381,6 +476,7 @@
/><span class="underline">View profile</span>
</button>
</li>
<li class="text-xs text-gray-500">
{#if userState.loginMethod === "extension"}
Logged in with extension

73
src/lib/stores/userStore.ts

@ -163,10 +163,14 @@ export async function loginWithExtension() { @@ -163,10 +163,14 @@ export async function loginWithExtension() {
const user = await signer.user();
const npub = user.npub;
console.log("Login with extension - fetching profile for npub:", npub);
// Try to fetch user metadata, but don't fail if it times out
let profile: NostrProfile | null = null;
try {
profile = await getUserMetadata(npub);
console.log("Login with extension - attempting to fetch profile...");
profile = await getUserMetadata(npub, true); // Force fresh fetch
console.log("Login with extension - fetched profile:", profile);
} catch (error) {
console.warn("Failed to fetch user metadata during login:", error);
// Continue with login even if metadata fetch fails
@ -174,6 +178,7 @@ export async function loginWithExtension() { @@ -174,6 +178,7 @@ export async function loginWithExtension() {
name: npub.slice(0, 8) + "..." + npub.slice(-4),
displayName: npub.slice(0, 8) + "..." + npub.slice(-4),
};
console.log("Login with extension - using fallback profile:", profile);
}
// Fetch user's preferred relays
@ -185,7 +190,8 @@ export async function loginWithExtension() { @@ -185,7 +190,8 @@ export async function loginWithExtension() {
persistRelays(user, inboxes, outboxes);
ndk.signer = signer;
ndk.activeUser = user;
userStore.set({
const userState = {
pubkey: user.pubkey,
npub,
profile,
@ -195,11 +201,14 @@ export async function loginWithExtension() { @@ -195,11 +201,14 @@ export async function loginWithExtension() {
(relay) => relay.url,
),
},
loginMethod: "extension",
loginMethod: "extension" as const,
ndkUser: user,
signer,
signedIn: true,
});
};
console.log("Login with extension - setting userStore with:", userState);
userStore.set(userState);
clearLogin();
localStorage.removeItem("alexandria/logout/flag");
persistLogin(user, "extension");
@ -213,7 +222,23 @@ export async function loginWithAmber(amberSigner: NDKSigner, user: NDKUser) { @@ -213,7 +222,23 @@ export async function loginWithAmber(amberSigner: NDKSigner, user: NDKUser) {
if (!ndk) throw new Error("NDK not initialized");
// Only clear previous login state after successful login
const npub = user.npub;
const profile = await getUserMetadata(npub, true); // Force fresh fetch
console.log("Login with Amber - fetching profile for npub:", npub);
let profile: NostrProfile | null = null;
try {
profile = await getUserMetadata(npub, true); // Force fresh fetch
console.log("Login with Amber - fetched profile:", profile);
} catch (error) {
console.warn("Failed to fetch user metadata during Amber login:", error);
// Continue with login even if metadata fetch fails
profile = {
name: npub.slice(0, 8) + "..." + npub.slice(-4),
displayName: npub.slice(0, 8) + "..." + npub.slice(-4),
};
console.log("Login with Amber - using fallback profile:", profile);
}
const [persistedInboxes, persistedOutboxes] = getPersistedRelays(user);
for (const relay of persistedInboxes) {
ndk.addExplicitRelay(relay);
@ -222,7 +247,8 @@ export async function loginWithAmber(amberSigner: NDKSigner, user: NDKUser) { @@ -222,7 +247,8 @@ export async function loginWithAmber(amberSigner: NDKSigner, user: NDKUser) {
persistRelays(user, inboxes, outboxes);
ndk.signer = amberSigner;
ndk.activeUser = user;
userStore.set({
const userState = {
pubkey: user.pubkey,
npub,
profile,
@ -232,11 +258,14 @@ export async function loginWithAmber(amberSigner: NDKSigner, user: NDKUser) { @@ -232,11 +258,14 @@ export async function loginWithAmber(amberSigner: NDKSigner, user: NDKUser) {
(relay) => relay.url,
),
},
loginMethod: "amber",
loginMethod: "amber" as const,
ndkUser: user,
signer: amberSigner,
signedIn: true,
});
};
console.log("Login with Amber - setting userStore with:", userState);
userStore.set(userState);
clearLogin();
localStorage.removeItem("alexandria/logout/flag");
persistLogin(user, "amber");
@ -267,20 +296,40 @@ export async function loginWithNpub(pubkeyOrNpub: string) { @@ -267,20 +296,40 @@ export async function loginWithNpub(pubkeyOrNpub: string) {
console.error("Failed to encode npub from hex pubkey:", hexPubkey, e);
throw e;
}
console.log("Login with npub - fetching profile for npub:", npub);
const user = ndk.getUser({ npub });
const profile = await getUserMetadata(npub);
let profile: NostrProfile | null = null;
try {
profile = await getUserMetadata(npub, true); // Force fresh fetch
console.log("Login with npub - fetched profile:", profile);
} catch (error) {
console.warn("Failed to fetch user metadata during npub login:", error);
// Continue with login even if metadata fetch fails
profile = {
name: npub.slice(0, 8) + "..." + npub.slice(-4),
displayName: npub.slice(0, 8) + "..." + npub.slice(-4),
};
console.log("Login with npub - using fallback profile:", profile);
}
ndk.signer = undefined;
ndk.activeUser = user;
userStore.set({
const userState = {
pubkey: user.pubkey,
npub,
profile,
relays: { inbox: [], outbox: [] },
loginMethod: "npub",
loginMethod: "npub" as const,
ndkUser: user,
signer: null,
signedIn: true,
});
};
console.log("Login with npub - setting userStore with:", userState);
userStore.set(userState);
clearLogin();
localStorage.removeItem("alexandria/logout/flag");
persistLogin(user, "npub");

35
src/lib/utils/nostrUtils.ts

@ -60,8 +60,12 @@ export async function getUserMetadata( @@ -60,8 +60,12 @@ export async function getUserMetadata(
// Remove nostr: prefix if present
const cleanId = identifier.replace(/^nostr:/, "");
console.log("getUserMetadata called with identifier:", identifier, "force:", force);
if (!force && npubCache.has(cleanId)) {
return npubCache.get(cleanId)!;
const cached = npubCache.get(cleanId)!;
console.log("getUserMetadata returning cached profile:", cached);
return cached;
}
const fallback = { name: `${cleanId.slice(0, 8)}...${cleanId.slice(-4)}` };
@ -69,12 +73,14 @@ export async function getUserMetadata( @@ -69,12 +73,14 @@ export async function getUserMetadata(
try {
const ndk = get(ndkInstance);
if (!ndk) {
console.warn("getUserMetadata: No NDK instance available");
npubCache.set(cleanId, fallback);
return fallback;
}
const decoded = nip19.decode(cleanId);
if (!decoded) {
console.warn("getUserMetadata: Failed to decode identifier:", cleanId);
npubCache.set(cleanId, fallback);
return fallback;
}
@ -86,19 +92,27 @@ export async function getUserMetadata( @@ -86,19 +92,27 @@ export async function getUserMetadata(
} else if (decoded.type === "nprofile") {
pubkey = decoded.data.pubkey;
} else {
console.warn("getUserMetadata: Unsupported identifier type:", decoded.type);
npubCache.set(cleanId, fallback);
return fallback;
}
console.log("getUserMetadata: Fetching profile for pubkey:", pubkey);
const profileEvent = await fetchEventWithFallback(ndk, {
kinds: [0],
authors: [pubkey],
});
console.log("getUserMetadata: Profile event found:", profileEvent);
const profile =
profileEvent && profileEvent.content
? JSON.parse(profileEvent.content)
: null;
console.log("getUserMetadata: Parsed profile:", profile);
const metadata: NostrProfile = {
name: profile?.name || fallback.name,
displayName: profile?.displayName || profile?.display_name,
@ -110,9 +124,11 @@ export async function getUserMetadata( @@ -110,9 +124,11 @@ export async function getUserMetadata(
lud16: profile?.lud16,
};
console.log("getUserMetadata: Final metadata:", metadata);
npubCache.set(cleanId, metadata);
return metadata;
} catch (e) {
console.error("getUserMetadata: Error fetching profile:", e);
npubCache.set(cleanId, fallback);
return fallback;
}
@ -426,9 +442,11 @@ export async function fetchEventWithFallback( @@ -426,9 +442,11 @@ export async function fetchEventWithFallback(
// Use the active inbox relays from the relay management system
const inboxRelays = get(activeInboxRelays);
console.log("fetchEventWithFallback: Using inbox relays:", inboxRelays);
// Check if we have any relays available
if (inboxRelays.length === 0) {
console.warn("No inbox relays available for event fetch");
console.warn("fetchEventWithFallback: No inbox relays available for event fetch");
return null;
}
@ -437,10 +455,14 @@ export async function fetchEventWithFallback( @@ -437,10 +455,14 @@ export async function fetchEventWithFallback(
try {
if (relaySet.relays.size === 0) {
console.warn("No relays in relay set for event fetch");
console.warn("fetchEventWithFallback: No relays in relay set for event fetch");
return null;
}
console.log("fetchEventWithFallback: Relay set size:", relaySet.relays.size);
console.log("fetchEventWithFallback: Filter:", filterOrId);
console.log("fetchEventWithFallback: Relay URLs:", Array.from(relaySet.relays).map((r: any) => r.url));
let found: NDKEvent | null = null;
if (
@ -465,11 +487,12 @@ export async function fetchEventWithFallback( @@ -465,11 +487,12 @@ export async function fetchEventWithFallback(
const timeoutSeconds = timeoutMs / 1000;
const relayUrls = Array.from(relaySet.relays).map((r: any) => r.url).join(", ");
console.warn(
`Event not found after ${timeoutSeconds}s timeout. Tried inbox relays: ${relayUrls}. Some relays may be offline or slow.`,
`fetchEventWithFallback: Event not found after ${timeoutSeconds}s timeout. Tried inbox relays: ${relayUrls}. Some relays may be offline or slow.`,
);
return null;
}
console.log("fetchEventWithFallback: Found event:", found.id);
// Always wrap as NDKEvent
return found instanceof NDKEvent ? found : new NDKEvent(ndk, found);
} catch (err) {
@ -477,10 +500,10 @@ export async function fetchEventWithFallback( @@ -477,10 +500,10 @@ export async function fetchEventWithFallback(
const timeoutSeconds = timeoutMs / 1000;
const relayUrls = Array.from(relaySet.relays).map((r: any) => r.url).join(", ");
console.warn(
`Event fetch timed out after ${timeoutSeconds}s. Tried inbox relays: ${relayUrls}. Some relays may be offline or slow.`,
`fetchEventWithFallback: Event fetch timed out after ${timeoutSeconds}s. Tried inbox relays: ${relayUrls}. Some relays may be offline or slow.`,
);
} else {
console.error("Error in fetchEventWithFallback:", err);
console.error("fetchEventWithFallback: Error in fetchEventWithFallback:", err);
}
return null;
}

Loading…
Cancel
Save