Browse Source

fixed profile display

master
silberengel 7 months ago
parent
commit
3eafdf0282
  1. 134
      src/lib/components/CommentViewer.svelte
  2. 28
      src/lib/components/EventDetails.svelte
  3. 491
      src/routes/events/+page.svelte

134
src/lib/components/CommentViewer.svelte

@ -36,12 +36,24 @@
const npub = toNpub(pubkey); const npub = toNpub(pubkey);
if (!npub) return; 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); const newProfiles = new Map(profiles);
newProfiles.set(pubkey, profile); newProfiles.set(pubkey, profile);
profiles = newProfiles; profiles = newProfiles;
console.log(`[CommentViewer] Fetched profile for ${pubkey}:`, profile);
} catch (err) { } catch (err) {
console.warn(`Failed to fetch profile for ${pubkey}:`, 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 = []; comments = [];
console.log(`[CommentViewer] Fetching comments for event: ${event.id}`); 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 // Wait for relays to be available
let attempts = 0; let attempts = 0;
@ -69,12 +84,35 @@
} }
try { try {
activeSub = $ndkInstance.subscribe({ // Try multiple filter approaches to find comments
kinds: [1, 1111], const filters = [
"#e": [event.id], // 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(() => { const timeout = setTimeout(() => {
console.log(`[CommentViewer] Subscription timeout - no comments found`);
if (activeSub) { if (activeSub) {
activeSub.stop(); activeSub.stop();
activeSub = null; activeSub = null;
@ -84,8 +122,21 @@
activeSub.on("event", (commentEvent: NDKEvent) => { activeSub.on("event", (commentEvent: NDKEvent) => {
console.log(`[CommentViewer] Received comment: ${commentEvent.id}`); console.log(`[CommentViewer] Received comment: ${commentEvent.id}`);
comments = [...comments, commentEvent]; console.log(`[CommentViewer] Comment kind: ${commentEvent.kind}`);
fetchProfile(commentEvent.pubkey); 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", () => { activeSub.on("eose", () => {
@ -96,6 +147,14 @@
activeSub = null; activeSub = null;
} }
loading = false; 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) => { activeSub.on("error", (err: any) => {
@ -116,6 +175,60 @@
} }
} }
// Pre-fetch all profiles for comments
async function preFetchAllProfiles() {
const uniquePubkeys = new Set<string>();
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 // Build threaded comment structure
function buildCommentThread(events: NDKEvent[]): CommentNode[] { function buildCommentThread(events: NDKEvent[]): CommentNode[] {
if (events.length === 0) return []; if (events.length === 0) return [];
@ -220,6 +333,9 @@
return neventEncode(commentEvent, $activeInboxRelays); 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) { function navigateToComment(commentEvent: NDKEvent) {
const nevent = getNeventUrl(commentEvent); const nevent = getNeventUrl(commentEvent);
goto(`/events?id=${encodeURIComponent(nevent)}`); goto(`/events?id=${encodeURIComponent(nevent)}`);
@ -275,7 +391,9 @@
function getAuthorName(pubkey: string): string { function getAuthorName(pubkey: string): string {
const profile = profiles.get(pubkey); 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 { function getAuthorPicture(pubkey: string): string | null {

28
src/lib/components/EventDetails.svelte

@ -449,20 +449,22 @@
<ContainingIndexes {event} /> <ContainingIndexes {event} />
<!-- Content --> <!-- Content -->
<div class="flex flex-col space-y-1"> {#if event.kind !== 0}
{#if event.kind !== 0} <div class="card-leather bg-highlight dark:bg-primary-800 p-4 mb-4 rounded-lg border">
<span class="text-gray-700 dark:text-gray-300">Content:</span> <div class="flex flex-col space-y-1">
<div class="prose dark:prose-invert max-w-none"> <span class="text-gray-700 dark:text-gray-300 font-semibold">Content:</span>
{@html showFullContent ? parsedContent : contentPreview} <div class="prose dark:prose-invert max-w-none">
{#if !showFullContent && parsedContent.length > 250} {@html showFullContent ? parsedContent : contentPreview}
<button {#if !showFullContent && parsedContent.length > 250}
class="mt-2 text-primary-700 hover:text-primary-900 dark:text-primary-400 dark:hover:text-primary-200" <button
onclick={() => (showFullContent = true)}>Show more</button class="mt-2 text-primary-700 hover:text-primary-900 dark:text-primary-400 dark:hover:text-primary-200"
> onclick={() => (showFullContent = true)}>Show more</button
{/if} >
{/if}
</div>
</div> </div>
{/if} </div>
</div> {/if}
<!-- If event is profile --> <!-- If event is profile -->
{#if event.kind === 0} {#if event.kind === 0}

491
src/routes/events/+page.svelte

@ -11,7 +11,7 @@
import CommentViewer from "$lib/components/CommentViewer.svelte"; import CommentViewer from "$lib/components/CommentViewer.svelte";
import { userStore } from "$lib/stores/userStore"; import { userStore } from "$lib/stores/userStore";
import { userBadge } from "$lib/snippets/UserSnippets.svelte"; 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 EventInput from "$lib/components/EventInput.svelte";
import { userPubkey, isLoggedIn } from "$lib/stores/authStore.Svelte"; import { userPubkey, isLoggedIn } from "$lib/stores/authStore.Svelte";
import CopyToClipboard from "$lib/components/util/CopyToClipboard.svelte"; import CopyToClipboard from "$lib/components/util/CopyToClipboard.svelte";
@ -75,6 +75,25 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
} else { } else {
profile = null; 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 // 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); 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 // Don't clear the current event - let the user continue viewing it
// event = null; // event = null;
// profile = null; // profile = null;
} }
// AI-NOTE: 2025-01-24 - Function to cache profiles for multiple events
async function cacheProfilesForEvents(events: NDKEvent[]) {
const uniquePubkeys = new Set<string>();
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() { function handleClear() {
searchType = null; searchType = null;
searchTerm = null; searchTerm = null;
@ -233,48 +273,47 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
originalEventIds: Set<string>, originalEventIds: Set<string>,
originalAddresses: Set<string>, originalAddresses: Set<string>,
): string { ): string {
// Check if this event has e-tags referencing original events const eTags = event.getMatchingTags("e");
const eTags = getMatchingTags(event, "e"); const aTags = event.getMatchingTags("a");
for (const tag of eTags) {
if (originalEventIds.has(tag[1])) {
return "Reply/Reference (e-tag)";
}
}
// Check if this event has a-tags or e-tags referencing original events if (eTags.length > 0) {
let tags = getMatchingTags(event, "a"); const referencedEventId = eTags[eTags.length - 1][1];
if (tags.length === 0) { if (originalEventIds.has(referencedEventId)) {
tags = getMatchingTags(event, "e"); return "Reply";
}
} }
for (const tag of tags) { if (aTags.length > 0) {
if (originalAddresses.has(tag[1])) { const referencedAddress = aTags[aTags.length - 1][1];
return "Reply/Reference (a-tag)"; if (originalAddresses.has(referencedAddress)) {
return "Quote";
} }
} }
// Check if this event has content references return "Reference";
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";
}
}
for (const address of originalAddresses) { // AI-NOTE: 2025-01-24 - Function to parse profile content from kind 0 events
const naddrPattern = new RegExp(`naddr1[a-z0-9]{50,}`, "i"); function parseProfileContent(event: NDKEvent): {
if (naddrPattern.test(event.content)) { name?: string;
return "Content Reference"; 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 { function getNeventUrl(event: NDKEvent): string {
@ -427,6 +466,7 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
</Heading> </Heading>
<div class="space-y-4"> <div class="space-y-4">
{#each searchResults as result, index} {#each searchResults as result, index}
{@const profileData = parseProfileContent(result)}
<button <button
class="w-full text-left border border-gray-300 dark:border-gray-600 rounded-lg p-4 bg-white dark:bg-primary-900/70 hover:bg-gray-100 dark:hover:bg-primary-800 focus:bg-gray-100 dark:focus:bg-primary-800 focus:outline-none focus:ring-2 focus:ring-primary-500 transition-colors overflow-hidden" class="w-full text-left border border-gray-300 dark:border-gray-600 rounded-lg p-4 bg-white dark:bg-primary-900/70 hover:bg-gray-100 dark:hover:bg-primary-800 focus:bg-gray-100 dark:focus:bg-primary-800 focus:outline-none focus:ring-2 focus:ring-primary-500 transition-colors overflow-hidden"
onclick={() => handleEventFound(result)} onclick={() => handleEventFound(result)}
@ -461,7 +501,7 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
<span class="text-xs text-gray-600 dark:text-gray-400"> <span class="text-xs text-gray-600 dark:text-gray-400">
{@render userBadge( {@render userBadge(
toNpub(result.pubkey) as string, toNpub(result.pubkey) as string,
undefined, profileData?.display_name || profileData?.name,
)} )}
</span> </span>
<span <span
@ -474,58 +514,91 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
: "Unknown date"} : "Unknown date"}
</span> </span>
</div> </div>
{#if getSummary(result)} {#if result.kind === 0 && profileData}
<div <div class="flex items-center gap-3 mb-2">
class="text-sm text-primary-900 dark:text-primary-200 mb-1 line-clamp-2" {#if profileData.picture}
> <img
{getSummary(result)} src={profileData.picture}
alt="Profile"
class="w-12 h-12 rounded-full object-cover border border-gray-200 dark:border-gray-600"
onerror={(e) => {
(e.target as HTMLImageElement).style.display = 'none';
}}
/>
{:else}
<div class="w-12 h-12 rounded-full bg-gray-300 dark:bg-gray-600 flex items-center justify-center border border-gray-200 dark:border-gray-600">
<span class="text-lg font-medium text-gray-600 dark:text-gray-300">
{(profileData.display_name || profileData.name || result.pubkey.slice(0, 1)).toUpperCase()}
</span>
</div>
{/if}
<div class="flex flex-col min-w-0 flex-1">
{#if profileData.display_name || profileData.name}
<span class="font-medium text-gray-900 dark:text-gray-100 truncate">
{profileData.display_name || profileData.name}
</span>
{/if}
{#if profileData.about}
<span class="text-sm text-gray-600 dark:text-gray-400 line-clamp-2">
{profileData.about}
</span>
{/if}
</div>
</div> </div>
{/if} {:else}
{#if getDeferralNaddr(result)} {#if getSummary(result)}
<div <div
class="text-xs text-primary-800 dark:text-primary-300 mb-1" class="text-sm text-primary-900 dark:text-primary-200 mb-1 line-clamp-2"
> >
Read {getSummary(result)}
<span </div>
class="underline text-primary-700 dark:text-primary-400 hover:text-primary-900 dark:hover:text-primary-200 break-all cursor-pointer" {/if}
onclick={(e) => { {#if getDeferralNaddr(result)}
e.stopPropagation(); <div
navigateToPublication( class="text-xs text-primary-800 dark:text-primary-300 mb-1"
getDeferralNaddr(result) || "", >
); Read
}} <span
onkeydown={(e) => { class="underline text-primary-700 dark:text-primary-400 hover:text-primary-900 dark:hover:text-primary-200 break-all cursor-pointer"
if (e.key === "Enter" || e.key === " ") { onclick={(e) => {
e.preventDefault();
e.stopPropagation(); e.stopPropagation();
navigateToPublication( navigateToPublication(
getDeferralNaddr(result) || "", getDeferralNaddr(result) || "",
); );
} }}
}} onkeydown={(e) => {
tabindex="0" if (e.key === "Enter" || e.key === " ") {
role="button" e.preventDefault();
e.stopPropagation();
navigateToPublication(
getDeferralNaddr(result) || "",
);
}
}}
tabindex="0"
role="button"
>
{getDeferralNaddr(result)}
</span>
</div>
{/if}
{#if isAddressableEvent(result)}
<div
class="text-xs text-blue-600 dark:text-blue-400 mb-1"
> >
{getDeferralNaddr(result)} <ViewPublicationLink event={result} />
</span> </div>
</div> {/if}
{/if} {#if result.content}
{#if isAddressableEvent(result)} <div
<div class="text-sm text-gray-800 dark:text-gray-200 mt-1 line-clamp-2 break-words"
class="text-xs text-blue-600 dark:text-blue-400 mb-1" >
> {result.content.slice(0, 200)}{result.content.length >
<ViewPublicationLink event={result} /> 200
</div> ? "..."
{/if} : ""}
{#if result.content} </div>
<div {/if}
class="text-sm text-gray-800 dark:text-gray-200 mt-1 line-clamp-2 break-words"
>
{result.content.slice(0, 200)}{result.content.length >
200
? "..."
: ""}
</div>
{/if} {/if}
</div> </div>
</button> </button>
@ -551,6 +624,7 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
</P> </P>
<div class="space-y-4"> <div class="space-y-4">
{#each secondOrderResults as result, index} {#each secondOrderResults as result, index}
{@const profileData = parseProfileContent(result)}
<button <button
class="w-full text-left border border-gray-300 dark:border-gray-600 rounded-lg p-4 bg-gray-50 dark:bg-primary-800/50 hover:bg-gray-100 dark:hover:bg-primary-700 focus:bg-gray-100 dark:focus:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 transition-colors overflow-hidden" class="w-full text-left border border-gray-300 dark:border-gray-600 rounded-lg p-4 bg-gray-50 dark:bg-primary-800/50 hover:bg-gray-100 dark:hover:bg-primary-700 focus:bg-gray-100 dark:focus:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 transition-colors overflow-hidden"
onclick={() => handleEventFound(result)} onclick={() => handleEventFound(result)}
@ -584,7 +658,7 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
<span class="text-xs text-gray-600 dark:text-gray-400"> <span class="text-xs text-gray-600 dark:text-gray-400">
{@render userBadge( {@render userBadge(
toNpub(result.pubkey) as string, toNpub(result.pubkey) as string,
undefined, profileData?.display_name || profileData?.name,
)} )}
</span> </span>
<span <span
@ -604,58 +678,91 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
originalAddresses, originalAddresses,
)} )}
</div> </div>
{#if getSummary(result)} {#if result.kind === 0 && profileData}
<div <div class="flex items-center gap-3 mb-2">
class="text-sm text-primary-900 dark:text-primary-200 mb-1 line-clamp-2" {#if profileData.picture}
> <img
{getSummary(result)} src={profileData.picture}
alt="Profile"
class="w-12 h-12 rounded-full object-cover border border-gray-200 dark:border-gray-600"
onerror={(e) => {
(e.target as HTMLImageElement).style.display = 'none';
}}
/>
{:else}
<div class="w-12 h-12 rounded-full bg-gray-300 dark:bg-gray-600 flex items-center justify-center border border-gray-200 dark:border-gray-600">
<span class="text-lg font-medium text-gray-600 dark:text-gray-300">
{(profileData.display_name || profileData.name || result.pubkey.slice(0, 1)).toUpperCase()}
</span>
</div>
{/if}
<div class="flex flex-col min-w-0 flex-1">
{#if profileData.display_name || profileData.name}
<span class="font-medium text-gray-900 dark:text-gray-100 truncate">
{profileData.display_name || profileData.name}
</span>
{/if}
{#if profileData.about}
<span class="text-sm text-gray-600 dark:text-gray-400 line-clamp-2">
{profileData.about}
</span>
{/if}
</div>
</div> </div>
{/if} {:else}
{#if getDeferralNaddr(result)} {#if getSummary(result)}
<div <div
class="text-xs text-primary-800 dark:text-primary-300 mb-1" class="text-sm text-primary-900 dark:text-primary-200 mb-1 line-clamp-2"
> >
Read {getSummary(result)}
<span </div>
class="underline text-primary-700 dark:text-primary-400 hover:text-primary-900 dark:hover:text-primary-200 break-all cursor-pointer" {/if}
onclick={(e) => { {#if getDeferralNaddr(result)}
e.stopPropagation(); <div
navigateToPublication( class="text-xs text-primary-800 dark:text-primary-300 mb-1"
getDeferralNaddr(result) || "", >
); Read
}} <span
onkeydown={(e) => { class="underline text-primary-700 dark:text-primary-400 hover:text-primary-900 dark:hover:text-primary-200 break-all cursor-pointer"
if (e.key === "Enter" || e.key === " ") { onclick={(e) => {
e.preventDefault();
e.stopPropagation(); e.stopPropagation();
navigateToPublication( navigateToPublication(
getDeferralNaddr(result) || "", getDeferralNaddr(result) || "",
); );
} }}
}} onkeydown={(e) => {
tabindex="0" if (e.key === "Enter" || e.key === " ") {
role="button" e.preventDefault();
e.stopPropagation();
navigateToPublication(
getDeferralNaddr(result) || "",
);
}
}}
tabindex="0"
role="button"
>
{getDeferralNaddr(result)}
</span>
</div>
{/if}
{#if isAddressableEvent(result)}
<div
class="text-xs text-blue-600 dark:text-blue-400 mb-1"
> >
{getDeferralNaddr(result)} <ViewPublicationLink event={result} />
</span> </div>
</div> {/if}
{/if} {#if result.content}
{#if isAddressableEvent(result)} <div
<div class="text-sm text-gray-800 dark:text-gray-200 mt-1 line-clamp-2 break-words"
class="text-xs text-blue-600 dark:text-blue-400 mb-1" >
> {result.content.slice(0, 200)}{result.content.length >
<ViewPublicationLink event={result} /> 200
</div> ? "..."
{/if} : ""}
{#if result.content} </div>
<div {/if}
class="text-sm text-gray-800 dark:text-gray-200 mt-1 line-clamp-2 break-words"
>
{result.content.slice(0, 200)}{result.content.length >
200
? "..."
: ""}
</div>
{/if} {/if}
</div> </div>
</button> </button>
@ -675,6 +782,7 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
</P> </P>
<div class="space-y-4"> <div class="space-y-4">
{#each tTagResults as result, index} {#each tTagResults as result, index}
{@const profileData = parseProfileContent(result)}
<button <button
class="w-full text-left border border-gray-300 dark:border-gray-600 rounded-lg p-4 bg-gray-50 dark:bg-primary-800/50 hover:bg-gray-100 dark:hover:bg-primary-700 focus:bg-gray-100 dark:focus:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 transition-colors overflow-hidden" class="w-full text-left border border-gray-300 dark:border-gray-600 rounded-lg p-4 bg-gray-50 dark:bg-primary-800/50 hover:bg-gray-100 dark:hover:bg-primary-700 focus:bg-gray-100 dark:focus:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 transition-colors overflow-hidden"
onclick={() => handleEventFound(result)} onclick={() => handleEventFound(result)}
@ -708,7 +816,7 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
<span class="text-xs text-gray-600 dark:text-gray-400"> <span class="text-xs text-gray-600 dark:text-gray-400">
{@render userBadge( {@render userBadge(
toNpub(result.pubkey) as string, toNpub(result.pubkey) as string,
undefined, profileData?.display_name || profileData?.name,
)} )}
</span> </span>
<span <span
@ -721,58 +829,91 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
: "Unknown date"} : "Unknown date"}
</span> </span>
</div> </div>
{#if getSummary(result)} {#if result.kind === 0 && profileData}
<div <div class="flex items-center gap-3 mb-2">
class="text-sm text-primary-900 dark:text-primary-200 mb-1 line-clamp-2" {#if profileData.picture}
> <img
{getSummary(result)} src={profileData.picture}
alt="Profile"
class="w-12 h-12 rounded-full object-cover border border-gray-200 dark:border-gray-600"
onerror={(e) => {
(e.target as HTMLImageElement).style.display = 'none';
}}
/>
{:else}
<div class="w-12 h-12 rounded-full bg-gray-300 dark:bg-gray-600 flex items-center justify-center border border-gray-200 dark:border-gray-600">
<span class="text-lg font-medium text-gray-600 dark:text-gray-300">
{(profileData.display_name || profileData.name || result.pubkey.slice(0, 1)).toUpperCase()}
</span>
</div>
{/if}
<div class="flex flex-col min-w-0 flex-1">
{#if profileData.display_name || profileData.name}
<span class="font-medium text-gray-900 dark:text-gray-100 truncate">
{profileData.display_name || profileData.name}
</span>
{/if}
{#if profileData.about}
<span class="text-sm text-gray-600 dark:text-gray-400 line-clamp-2">
{profileData.about}
</span>
{/if}
</div>
</div> </div>
{/if} {:else}
{#if getDeferralNaddr(result)} {#if getSummary(result)}
<div <div
class="text-xs text-primary-800 dark:text-primary-300 mb-1" class="text-sm text-primary-900 dark:text-primary-200 mb-1 line-clamp-2"
> >
Read {getSummary(result)}
<span </div>
class="underline text-primary-700 dark:text-primary-400 hover:text-primary-900 dark:hover:text-primary-200 break-all cursor-pointer" {/if}
onclick={(e) => { {#if getDeferralNaddr(result)}
e.stopPropagation(); <div
navigateToPublication( class="text-xs text-primary-800 dark:text-primary-300 mb-1"
getDeferralNaddr(result) || "", >
); Read
}} <span
onkeydown={(e) => { class="underline text-primary-700 dark:text-primary-400 hover:text-primary-900 dark:hover:text-primary-200 break-all cursor-pointer"
if (e.key === "Enter" || e.key === " ") { onclick={(e) => {
e.preventDefault();
e.stopPropagation(); e.stopPropagation();
navigateToPublication( navigateToPublication(
getDeferralNaddr(result) || "", getDeferralNaddr(result) || "",
); );
} }}
}} onkeydown={(e) => {
tabindex="0" if (e.key === "Enter" || e.key === " ") {
role="button" e.preventDefault();
e.stopPropagation();
navigateToPublication(
getDeferralNaddr(result) || "",
);
}
}}
tabindex="0"
role="button"
>
{getDeferralNaddr(result)}
</span>
</div>
{/if}
{#if isAddressableEvent(result)}
<div
class="text-xs text-blue-600 dark:text-blue-400 mb-1"
> >
{getDeferralNaddr(result)} <ViewPublicationLink event={result} />
</span> </div>
</div> {/if}
{/if} {#if result.content}
{#if isAddressableEvent(result)} <div
<div class="text-sm text-gray-800 dark:text-gray-200 mt-1 line-clamp-2 break-words"
class="text-xs text-blue-600 dark:text-blue-400 mb-1" >
> {result.content.slice(0, 200)}{result.content.length >
<ViewPublicationLink event={result} /> 200
</div> ? "..."
{/if} : ""}
{#if result.content} </div>
<div {/if}
class="text-sm text-gray-800 dark:text-gray-200 mt-1 line-clamp-2 break-words"
>
{result.content.slice(0, 200)}{result.content.length >
200
? "..."
: ""}
</div>
{/if} {/if}
</div> </div>
</button> </button>

Loading…
Cancel
Save