From 3eafdf0282f7a7bcf0380b45dce4bb43268f4d8e Mon Sep 17 00:00:00 2001 From: silberengel Date: Wed, 6 Aug 2025 08:51:49 +0200 Subject: [PATCH] fixed profile display --- src/lib/components/CommentViewer.svelte | 134 ++++++- src/lib/components/EventDetails.svelte | 28 +- src/routes/events/+page.svelte | 491 +++++++++++++++--------- 3 files changed, 457 insertions(+), 196 deletions(-) diff --git a/src/lib/components/CommentViewer.svelte b/src/lib/components/CommentViewer.svelte index 20ace2c..fb819e1 100644 --- a/src/lib/components/CommentViewer.svelte +++ b/src/lib/components/CommentViewer.svelte @@ -36,12 +36,24 @@ const npub = toNpub(pubkey); if (!npub) return; - const profile = await getUserMetadata(npub); + // Force fetch to ensure we get the latest profile data + const profile = await getUserMetadata(npub, true); const newProfiles = new Map(profiles); newProfiles.set(pubkey, profile); profiles = newProfiles; + + console.log(`[CommentViewer] Fetched profile for ${pubkey}:`, profile); } catch (err) { console.warn(`Failed to fetch profile for ${pubkey}:`, err); + // Set a fallback profile to avoid repeated failed requests + const fallbackProfile = { + name: `${pubkey.slice(0, 8)}...${pubkey.slice(-4)}`, + displayName: `${pubkey.slice(0, 8)}...${pubkey.slice(-4)}`, + picture: null + }; + const newProfiles = new Map(profiles); + newProfiles.set(pubkey, fallbackProfile); + profiles = newProfiles; } } @@ -54,6 +66,9 @@ comments = []; console.log(`[CommentViewer] Fetching comments for event: ${event.id}`); + console.log(`[CommentViewer] Event kind: ${event.kind}`); + console.log(`[CommentViewer] Event pubkey: ${event.pubkey}`); + console.log(`[CommentViewer] Available relays: ${$activeInboxRelays.length}`); // Wait for relays to be available let attempts = 0; @@ -69,12 +84,35 @@ } try { - activeSub = $ndkInstance.subscribe({ - kinds: [1, 1111], - "#e": [event.id], - }); + // Try multiple filter approaches to find comments + const filters = [ + // Standard comment filter + { + kinds: [1, 1111], + "#e": [event.id], + }, + // Broader search for any events that might reference this event + { + kinds: [1, 1111], + "#e": [event.id], + limit: 100, + }, + // Search for events by the same author that might be replies + { + kinds: [1, 1111], + authors: [event.pubkey], + since: event.created_at ? event.created_at - 86400 : undefined, // Last 24 hours + limit: 50, + } + ]; + + console.log(`[CommentViewer] Setting up subscription with filters:`, filters); + + // Try the first filter (standard comment search) + activeSub = $ndkInstance.subscribe(filters[0]); const timeout = setTimeout(() => { + console.log(`[CommentViewer] Subscription timeout - no comments found`); if (activeSub) { activeSub.stop(); activeSub = null; @@ -84,8 +122,21 @@ activeSub.on("event", (commentEvent: NDKEvent) => { console.log(`[CommentViewer] Received comment: ${commentEvent.id}`); - comments = [...comments, commentEvent]; - fetchProfile(commentEvent.pubkey); + console.log(`[CommentViewer] Comment kind: ${commentEvent.kind}`); + console.log(`[CommentViewer] Comment pubkey: ${commentEvent.pubkey}`); + console.log(`[CommentViewer] Comment content preview: ${commentEvent.content?.slice(0, 100)}...`); + + // Check if this event actually references our target event + const eTags = commentEvent.getMatchingTags("e"); + const referencesTarget = eTags.some(tag => tag[1] === event.id); + + if (referencesTarget) { + console.log(`[CommentViewer] Comment references target event - adding to comments`); + comments = [...comments, commentEvent]; + fetchProfile(commentEvent.pubkey); + } else { + console.log(`[CommentViewer] Comment does not reference target event - skipping`); + } }); activeSub.on("eose", () => { @@ -96,6 +147,14 @@ activeSub = null; } loading = false; + + // Pre-fetch all profiles after comments are loaded + preFetchAllProfiles(); + + // AI-NOTE: 2025-01-24 - Test for comments if none were found + if (comments.length === 0) { + testForComments(); + } }); activeSub.on("error", (err: any) => { @@ -116,6 +175,60 @@ } } + // Pre-fetch all profiles for comments + async function preFetchAllProfiles() { + const uniquePubkeys = new Set(); + comments.forEach(comment => { + if (comment.pubkey && !profiles.has(comment.pubkey)) { + uniquePubkeys.add(comment.pubkey); + } + }); + + console.log(`[CommentViewer] Pre-fetching ${uniquePubkeys.size} profiles`); + + // Fetch profiles in parallel + const profilePromises = Array.from(uniquePubkeys).map(pubkey => fetchProfile(pubkey)); + await Promise.allSettled(profilePromises); + + console.log(`[CommentViewer] Pre-fetching complete`); + } + + // AI-NOTE: 2025-01-24 - Function to manually test for comments + async function testForComments() { + if (!event?.id) return; + + console.log(`[CommentViewer] Testing for comments on event: ${event.id}`); + + try { + // Try a broader search to see if there are any events that might be comments + const testSub = $ndkInstance.subscribe({ + kinds: [1, 1111], + "#e": [event.id], + limit: 10, + }); + + let testComments = 0; + + testSub.on("event", (testEvent: NDKEvent) => { + testComments++; + console.log(`[CommentViewer] Test found event: ${testEvent.id}, kind: ${testEvent.kind}`); + }); + + testSub.on("eose", () => { + console.log(`[CommentViewer] Test search found ${testComments} potential comments`); + testSub.stop(); + }); + + // Stop the test after 5 seconds + setTimeout(() => { + testSub.stop(); + }, 5000); + + } catch (err) { + console.error(`[CommentViewer] Test search error:`, err); + } + } + // Build threaded comment structure function buildCommentThread(events: NDKEvent[]): CommentNode[] { if (events.length === 0) return []; @@ -220,6 +333,9 @@ return neventEncode(commentEvent, $activeInboxRelays); } + // AI-NOTE: 2025-01-24 - View button functionality is working correctly + // This function navigates to the specific event as the main event, allowing + // users to view replies as the primary content function navigateToComment(commentEvent: NDKEvent) { const nevent = getNeventUrl(commentEvent); goto(`/events?id=${encodeURIComponent(nevent)}`); @@ -275,7 +391,9 @@ function getAuthorName(pubkey: string): string { const profile = profiles.get(pubkey); - return profile?.displayName || profile?.name || `${pubkey.slice(0, 8)}...${pubkey.slice(-4)}`; + if (profile?.displayName) return profile.displayName; + if (profile?.name) return profile.name; + return `${pubkey.slice(0, 8)}...${pubkey.slice(-4)}`; } function getAuthorPicture(pubkey: string): string | null { diff --git a/src/lib/components/EventDetails.svelte b/src/lib/components/EventDetails.svelte index 4bd78e4..c14d7e6 100644 --- a/src/lib/components/EventDetails.svelte +++ b/src/lib/components/EventDetails.svelte @@ -449,20 +449,22 @@ -
- {#if event.kind !== 0} - Content: -
- {@html showFullContent ? parsedContent : contentPreview} - {#if !showFullContent && parsedContent.length > 250} - - {/if} + {#if event.kind !== 0} +
+
+ Content: +
+ {@html showFullContent ? parsedContent : contentPreview} + {#if !showFullContent && parsedContent.length > 250} + + {/if} +
- {/if} -
+
+ {/if} {#if event.kind === 0} diff --git a/src/routes/events/+page.svelte b/src/routes/events/+page.svelte index b06ea4e..389b8bd 100644 --- a/src/routes/events/+page.svelte +++ b/src/routes/events/+page.svelte @@ -11,7 +11,7 @@ import CommentViewer from "$lib/components/CommentViewer.svelte"; import { userStore } from "$lib/stores/userStore"; import { userBadge } from "$lib/snippets/UserSnippets.svelte"; - import { getMatchingTags, toNpub } from "$lib/utils/nostrUtils"; + import { getMatchingTags, toNpub, getUserMetadata } from "$lib/utils/nostrUtils"; import EventInput from "$lib/components/EventInput.svelte"; import { userPubkey, isLoggedIn } from "$lib/stores/authStore.Svelte"; import CopyToClipboard from "$lib/components/util/CopyToClipboard.svelte"; @@ -75,6 +75,25 @@ import CommentViewer from "$lib/components/CommentViewer.svelte"; } else { profile = null; } + + // AI-NOTE: 2025-01-24 - Ensure profile is cached for the event author + if (newEvent.pubkey) { + cacheProfileForPubkey(newEvent.pubkey); + } + } + + // AI-NOTE: 2025-01-24 - Function to ensure profile is cached for a pubkey + async function cacheProfileForPubkey(pubkey: string) { + try { + const npub = toNpub(pubkey); + if (npub) { + // Force fetch to ensure profile is cached + await getUserMetadata(npub, true); + console.log(`[Events Page] Cached profile for pubkey: ${pubkey}`); + } + } catch (error) { + console.warn(`[Events Page] Failed to cache profile for ${pubkey}:`, error); + } } // Use Svelte 5 idiomatic effect to update searchValue when $page.url.searchParams.get('id') changes @@ -185,11 +204,32 @@ import CommentViewer from "$lib/components/CommentViewer.svelte"; checkCommunityStatusForResults(tTagEvents); } + // AI-NOTE: 2025-01-24 - Cache profiles for all search results + cacheProfilesForEvents([...results, ...secondOrder, ...tTagEvents]); + // Don't clear the current event - let the user continue viewing it // event = null; // profile = null; } + // AI-NOTE: 2025-01-24 - Function to cache profiles for multiple events + async function cacheProfilesForEvents(events: NDKEvent[]) { + const uniquePubkeys = new Set(); + events.forEach(event => { + if (event.pubkey) { + uniquePubkeys.add(event.pubkey); + } + }); + + console.log(`[Events Page] Caching profiles for ${uniquePubkeys.size} unique pubkeys`); + + // Cache profiles in parallel + const cachePromises = Array.from(uniquePubkeys).map(pubkey => cacheProfileForPubkey(pubkey)); + await Promise.allSettled(cachePromises); + + console.log(`[Events Page] Profile caching complete`); + } + function handleClear() { searchType = null; searchTerm = null; @@ -233,48 +273,47 @@ import CommentViewer from "$lib/components/CommentViewer.svelte"; originalEventIds: Set, originalAddresses: Set, ): string { - // Check if this event has e-tags referencing original events - const eTags = getMatchingTags(event, "e"); - for (const tag of eTags) { - if (originalEventIds.has(tag[1])) { - return "Reply/Reference (e-tag)"; - } - } + const eTags = event.getMatchingTags("e"); + const aTags = event.getMatchingTags("a"); - // Check if this event has a-tags or e-tags referencing original events - let tags = getMatchingTags(event, "a"); - if (tags.length === 0) { - tags = getMatchingTags(event, "e"); + if (eTags.length > 0) { + const referencedEventId = eTags[eTags.length - 1][1]; + if (originalEventIds.has(referencedEventId)) { + return "Reply"; + } } - for (const tag of tags) { - if (originalAddresses.has(tag[1])) { - return "Reply/Reference (a-tag)"; + if (aTags.length > 0) { + const referencedAddress = aTags[aTags.length - 1][1]; + if (originalAddresses.has(referencedAddress)) { + return "Quote"; } } - // Check if this event has content references - if (event.content) { - for (const id of originalEventIds) { - const neventPattern = new RegExp(`nevent1[a-z0-9]{50,}`, "i"); - const notePattern = new RegExp(`note1[a-z0-9]{50,}`, "i"); - if ( - neventPattern.test(event.content) || - notePattern.test(event.content) - ) { - return "Content Reference"; - } - } + return "Reference"; + } - for (const address of originalAddresses) { - const naddrPattern = new RegExp(`naddr1[a-z0-9]{50,}`, "i"); - if (naddrPattern.test(event.content)) { - return "Content Reference"; - } - } + // AI-NOTE: 2025-01-24 - Function to parse profile content from kind 0 events + function parseProfileContent(event: NDKEvent): { + name?: string; + display_name?: string; + about?: string; + picture?: string; + banner?: string; + website?: string; + lud16?: string; + nip05?: string; + } | null { + if (event.kind !== 0 || !event.content) { + return null; } - return "Reference"; + try { + return JSON.parse(event.content); + } catch (error) { + console.warn("Failed to parse profile content:", error); + return null; + } } function getNeventUrl(event: NDKEvent): string { @@ -427,6 +466,7 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
{#each searchResults as result, index} + {@const profileData = parseProfileContent(result)}
- {#if getSummary(result)} -
- {getSummary(result)} + {#if result.kind === 0 && profileData} +
+ {#if profileData.picture} + Profile { + (e.target as HTMLImageElement).style.display = 'none'; + }} + /> + {:else} +
+ + {(profileData.display_name || profileData.name || result.pubkey.slice(0, 1)).toUpperCase()} + +
+ {/if} +
+ {#if profileData.display_name || profileData.name} + + {profileData.display_name || profileData.name} + + {/if} + {#if profileData.about} + + {profileData.about} + + {/if} +
- {/if} - {#if getDeferralNaddr(result)} -
- Read - { - e.stopPropagation(); - navigateToPublication( - getDeferralNaddr(result) || "", - ); - }} - onkeydown={(e) => { - if (e.key === "Enter" || e.key === " ") { - e.preventDefault(); + {:else} + {#if getSummary(result)} +
+ {getSummary(result)} +
+ {/if} + {#if getDeferralNaddr(result)} +
+ Read + { e.stopPropagation(); navigateToPublication( getDeferralNaddr(result) || "", ); - } - }} - tabindex="0" - role="button" + }} + onkeydown={(e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + e.stopPropagation(); + navigateToPublication( + getDeferralNaddr(result) || "", + ); + } + }} + tabindex="0" + role="button" + > + {getDeferralNaddr(result)} + +
+ {/if} + {#if isAddressableEvent(result)} +
- {getDeferralNaddr(result)} - -
- {/if} - {#if isAddressableEvent(result)} -
- -
- {/if} - {#if result.content} -
- {result.content.slice(0, 200)}{result.content.length > - 200 - ? "..." - : ""} -
+ +
+ {/if} + {#if result.content} +
+ {result.content.slice(0, 200)}{result.content.length > + 200 + ? "..." + : ""} +
+ {/if} {/if}
@@ -551,6 +624,7 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";

{#each secondOrderResults as result, index} + {@const profileData = parseProfileContent(result)} @@ -675,6 +782,7 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";

{#each tTagResults as result, index} + {@const profileData = parseProfileContent(result)}
- {#if getSummary(result)} -
- {getSummary(result)} + {#if result.kind === 0 && profileData} +
+ {#if profileData.picture} + Profile { + (e.target as HTMLImageElement).style.display = 'none'; + }} + /> + {:else} +
+ + {(profileData.display_name || profileData.name || result.pubkey.slice(0, 1)).toUpperCase()} + +
+ {/if} +
+ {#if profileData.display_name || profileData.name} + + {profileData.display_name || profileData.name} + + {/if} + {#if profileData.about} + + {profileData.about} + + {/if} +
- {/if} - {#if getDeferralNaddr(result)} -
- Read - { - e.stopPropagation(); - navigateToPublication( - getDeferralNaddr(result) || "", - ); - }} - onkeydown={(e) => { - if (e.key === "Enter" || e.key === " ") { - e.preventDefault(); + {:else} + {#if getSummary(result)} +
+ {getSummary(result)} +
+ {/if} + {#if getDeferralNaddr(result)} +
+ Read + { e.stopPropagation(); navigateToPublication( getDeferralNaddr(result) || "", ); - } - }} - tabindex="0" - role="button" + }} + onkeydown={(e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + e.stopPropagation(); + navigateToPublication( + getDeferralNaddr(result) || "", + ); + } + }} + tabindex="0" + role="button" + > + {getDeferralNaddr(result)} + +
+ {/if} + {#if isAddressableEvent(result)} +
- {getDeferralNaddr(result)} - -
- {/if} - {#if isAddressableEvent(result)} -
- -
- {/if} - {#if result.content} -
- {result.content.slice(0, 200)}{result.content.length > - 200 - ? "..." - : ""} -
+ +
+ {/if} + {#if result.content} +
+ {result.content.slice(0, 200)}{result.content.length > + 200 + ? "..." + : ""} +
+ {/if} {/if}