Browse Source

fix event result display

master
silberengel 7 months ago
parent
commit
0bd5a3f453
  1. 14
      src/lib/components/CommentViewer.svelte
  2. 186
      src/lib/components/EventDetails.svelte
  3. 18
      src/lib/components/RelayActions.svelte
  4. 128
      src/routes/events/+page.svelte

14
src/lib/components/CommentViewer.svelte

@ -8,6 +8,7 @@
import type { NDKEvent } from "@nostr-dev-kit/ndk"; import type { NDKEvent } from "@nostr-dev-kit/ndk";
import { userBadge } from "$lib/snippets/UserSnippets.svelte"; import { userBadge } from "$lib/snippets/UserSnippets.svelte";
import { parseBasicmarkup } from "$lib/utils/markup/basicMarkupParser"; import { parseBasicmarkup } from "$lib/utils/markup/basicMarkupParser";
import { parseRepostContent, parseContent as parseNotificationContent } from "$lib/utils/notification_utils";
const { event } = $props<{ event: NDKEvent }>(); const { event } = $props<{ event: NDKEvent }>();
@ -653,12 +654,15 @@
return `${actualLevel * 16}px`; return `${actualLevel * 16}px`;
} }
async function parseContent(content: string): Promise<string> { async function parseContent(content: string, eventKind?: number): Promise<string> {
if (!content) return ""; if (!content) return "";
let parsedContent = await parseBasicmarkup(content); // Use parseRepostContent for kind 6 and 16 events (reposts)
if (eventKind === 6 || eventKind === 16) {
return parsedContent; return await parseRepostContent(content);
} else {
return await parseNotificationContent(content);
}
} }
@ -825,7 +829,7 @@
</div> </div>
{:else} {:else}
<!-- Regular comment content --> <!-- Regular comment content -->
{#await parseContent(node.event.content || "") then parsedContent} {#await parseContent(node.event.content || "", node.event.kind) then parsedContent}
{@html parsedContent} {@html parsedContent}
{:catch} {:catch}
{@html node.event.content || ""} {@html node.event.content || ""}

186
src/lib/components/EventDetails.svelte

@ -15,6 +15,8 @@
import { navigateToEvent } from "$lib/utils/nostrEventService"; import { navigateToEvent } from "$lib/utils/nostrEventService";
import ContainingIndexes from "$lib/components/util/ContainingIndexes.svelte"; import ContainingIndexes from "$lib/components/util/ContainingIndexes.svelte";
import Notifications from "$lib/components/Notifications.svelte"; import Notifications from "$lib/components/Notifications.svelte";
import { parseRepostContent } from "$lib/utils/notification_utils";
import RelayActions from "$lib/components/RelayActions.svelte";
const { const {
event, event,
@ -305,10 +307,18 @@
$effect(() => { $effect(() => {
if (event && event.kind !== 0 && event.content) { if (event && event.kind !== 0 && event.content) {
parseBasicmarkup(event.content).then((html) => { // Use parseRepostContent for kind 6 and 16 events (reposts)
parsedContent = html; if (event.kind === 6 || event.kind === 16) {
contentPreview = html.slice(0, 250); parseRepostContent(event.content).then((html) => {
}); parsedContent = html;
contentPreview = html.slice(0, 250);
});
} else {
parseBasicmarkup(event.content).then((html) => {
parsedContent = html;
contentPreview = html.slice(0, 250);
});
}
} }
}); });
@ -436,30 +446,17 @@
</div> </div>
{/if} {/if}
{#if getEventHashtags(event).length}
<div class="flex flex-col space-y-1">
<span class="text-gray-700 dark:text-gray-300">Tags:</span>
<div class="flex flex-wrap gap-2">
{#each getEventHashtags(event) as tag}
<button
onclick={() => goto(`/events?t=${encodeURIComponent(tag)}`)}
class="px-2 py-1 rounded bg-primary-100 text-primary-800 text-sm font-medium hover:bg-primary-200 cursor-pointer"
>#{tag}</button
>
{/each}
</div>
</div>
{/if}
<!-- Containing Publications --> <!-- Containing Publications -->
<ContainingIndexes {event} /> <ContainingIndexes {event} />
<!-- Content --> <!-- Content -->
{#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"> <div class="card-leather bg-highlight dark:bg-primary-800 p-4 mb-4 rounded-lg border max-w-full overflow-hidden">
<div class="flex flex-col space-y-1"> <div class="flex flex-col space-y-1">
<span class="text-gray-700 dark:text-gray-300 font-semibold">Content:</span> <span class="text-gray-700 dark:text-gray-300 font-semibold">Content:</span>
<div class="prose dark:prose-invert max-w-none text-gray-900 dark:text-gray-100"> <div class="prose dark:prose-invert max-w-none text-gray-900 dark:text-gray-100 break-words overflow-wrap-anywhere">
{@html showFullContent ? parsedContent : contentPreview} {@html showFullContent ? parsedContent : contentPreview}
{#if !showFullContent && parsedContent.length > 250} {#if !showFullContent && parsedContent.length > 250}
<button <button
@ -477,78 +474,105 @@
<ProfileHeader <ProfileHeader
{event} {event}
{profile} {profile}
identifiers={getIdentifiers(event, profile)}
/> />
{/if} {/if}
<!-- Tags Array -->
{#if event.tags && event.tags.length}
<div class="flex flex-col space-y-1">
<span class="text-gray-700 dark:text-gray-300">Event Tags:</span>
<div class="flex flex-wrap gap-2">
{#each event.tags as tag}
{@const tagInfo = getTagButtonInfo(tag)}
{#if tagInfo.text && tagInfo.gotoValue}
<button
onclick={() => {
// Handle different types of gotoValue
if (
tagInfo.gotoValue!.startsWith("naddr") ||
tagInfo.gotoValue!.startsWith("nevent") ||
tagInfo.gotoValue!.startsWith("npub") ||
tagInfo.gotoValue!.startsWith("nprofile") ||
tagInfo.gotoValue!.startsWith("note")
) {
// For naddr, nevent, npub, nprofile, note - navigate directly
goto(`/events?id=${tagInfo.gotoValue!}`);
} else if (tagInfo.gotoValue!.startsWith("/")) {
// For relative URLs - navigate directly
goto(tagInfo.gotoValue!);
} else if (tagInfo.gotoValue!.startsWith("d:")) {
// For d-tag searches - navigate to d-tag search
const dTag = tagInfo.gotoValue!.substring(2);
goto(`/events?d=${encodeURIComponent(dTag)}`);
} else if (tagInfo.gotoValue!.startsWith("t:")) {
// For t-tag searches - navigate to t-tag search
const tTag = tagInfo.gotoValue!.substring(2);
goto(`/events?t=${encodeURIComponent(tTag)}`);
} else if (/^[0-9a-fA-F]{64}$/.test(tagInfo.gotoValue!)) {
// For hex event IDs - use navigateToEvent
navigateToEvent(tagInfo.gotoValue!);
} else {
// For other cases, try direct navigation
goto(`/events?id=${tagInfo.gotoValue!}`);
}
}}
class="text-primary-700 dark:text-primary-300 cursor-pointer bg-transparent border-none p-0 text-left hover:text-primary-900 dark:hover:text-primary-100"
>
{tagInfo.text}
</button>
{/if}
{/each}
</div>
</div>
{/if}
<!-- Raw Event JSON --> <!-- Raw Event JSON -->
<details <details
class="relative w-full max-w-2xl md:max-w-full bg-primary-50 dark:bg-primary-900 rounded p-4" class="relative w-full max-w-2xl md:max-w-full bg-primary-50 dark:bg-primary-900 rounded p-4 overflow-hidden"
> >
<summary <summary
class="cursor-pointer font-semibold text-primary-700 dark:text-primary-300 mb-2" class="cursor-pointer font-semibold text-primary-700 dark:text-primary-300 mb-2"
> >
Show Raw Event JSON Show details
</summary> </summary>
<div class="absolute top-4 right-4">
<CopyToClipboard <!-- Identifiers Section -->
displayText="" <div class="mb-4 max-w-full overflow-hidden">
copyText={JSON.stringify(event.rawEvent(), null, 2)} <h4 class="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2">Identifiers:</h4>
/> <div class="flex flex-col gap-2">
{#each getIdentifiers(event, profile) as identifier}
<div class="flex items-center gap-2 min-w-0">
<span class="text-gray-600 dark:text-gray-400 flex-shrink-0">{identifier.label}:</span>
<div class="flex-1 min-w-0 flex items-center gap-2">
<span class="font-mono text-sm text-gray-900 dark:text-gray-100" title={identifier.value}>
{identifier.value.slice(0, 20)}...{identifier.value.slice(-8)}
</span>
<CopyToClipboard
displayText=""
copyText={identifier.value}
/>
</div>
</div>
{/each}
</div>
</div> </div>
<pre
class="overflow-x-auto text-xs bg-highlight dark:bg-primary-900 rounded p-4 mt-2 font-mono" <!-- Event Tags Section -->
style="line-height: 1.7; font-size: 1rem;"> {#if event.tags && event.tags.length}
<div class="mb-4 max-w-full overflow-hidden">
<h4 class="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2">Event Tags:</h4>
<div class="flex flex-wrap gap-2 break-words">
{#each event.tags as tag}
{@const tagInfo = getTagButtonInfo(tag)}
{#if tagInfo.text && tagInfo.gotoValue}
<button
onclick={() => {
// Handle different types of gotoValue
if (
tagInfo.gotoValue!.startsWith("naddr") ||
tagInfo.gotoValue!.startsWith("nevent") ||
tagInfo.gotoValue!.startsWith("npub") ||
tagInfo.gotoValue!.startsWith("nprofile") ||
tagInfo.gotoValue!.startsWith("note")
) {
// For naddr, nevent, npub, nprofile, note - navigate directly
goto(`/events?id=${tagInfo.gotoValue!}`);
} else if (tagInfo.gotoValue!.startsWith("/")) {
// For relative URLs - navigate directly
goto(tagInfo.gotoValue!);
} else if (tagInfo.gotoValue!.startsWith("d:")) {
// For d-tag searches - navigate to d-tag search
const dTag = tagInfo.gotoValue!.substring(2);
goto(`/events?d=${encodeURIComponent(dTag)}`);
} else if (tagInfo.gotoValue!.startsWith("t:")) {
// For t-tag searches - navigate to t-tag search
const tTag = tagInfo.gotoValue!.substring(2);
goto(`/events?t=${encodeURIComponent(tTag)}`);
} else if (/^[0-9a-fA-F]{64}$/.test(tagInfo.gotoValue!)) {
// For hex event IDs - use navigateToEvent
navigateToEvent(tagInfo.gotoValue!);
} else {
// For other cases, try direct navigation
goto(`/events?id=${tagInfo.gotoValue!}`);
}
}}
class="text-primary-700 dark:text-primary-300 cursor-pointer bg-transparent border-none p-0 text-left hover:text-primary-900 dark:hover:text-primary-100 break-all"
>
{tagInfo.text}
</button>
{/if}
{/each}
</div>
</div>
{/if}
<!-- Raw Event JSON Section -->
<div class="mb-4 max-w-full overflow-hidden">
<h4 class="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2">Raw Event JSON:</h4>
<div class="relative">
<div class="absolute top-0 right-0 z-10">
<CopyToClipboard
displayText=""
copyText={JSON.stringify(event.rawEvent(), null, 2)}
/>
</div>
<pre
class="overflow-x-auto text-xs bg-highlight dark:bg-primary-900 rounded p-4 mt-2 font-mono break-words whitespace-pre-wrap"
style="line-height: 1.7; font-size: 1rem;">
{JSON.stringify(event.rawEvent(), null, 2)} {JSON.stringify(event.rawEvent(), null, 2)}
</pre> </pre>
</div>
</div>
</details> </details>
</div> </div>

18
src/lib/components/RelayActions.svelte

@ -72,24 +72,6 @@
} }
</script> </script>
<div class="mt-4 flex flex-wrap gap-2">
<Button onclick={openRelayModal} class="flex items-center">
{@html searchIcon}
Where can I find this event?
</Button>
</div>
{#if foundRelays.length > 0}
<div class="mt-2">
<span class="font-semibold">Found on {foundRelays.length} relay(s):</span>
<div class="flex flex-wrap gap-2 mt-1">
{#each foundRelays as relay}
<RelayDisplay {relay} />
{/each}
</div>
</div>
{/if}
<div class="mt-2"> <div class="mt-2">
<span class="font-semibold">Found on:</span> <span class="font-semibold">Found on:</span>
<div class="flex flex-wrap gap-2 mt-1"> <div class="flex flex-wrap gap-2 mt-1">

128
src/routes/events/+page.svelte

@ -20,6 +20,7 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
import { getEventType } from "$lib/utils/mime"; import { getEventType } from "$lib/utils/mime";
import ViewPublicationLink from "$lib/components/util/ViewPublicationLink.svelte"; import ViewPublicationLink from "$lib/components/util/ViewPublicationLink.svelte";
import { checkCommunity } from "$lib/utils/search_utility"; import { checkCommunity } from "$lib/utils/search_utility";
import { parseRepostContent, parseContent } from "$lib/utils/notification_utils";
let loading = $state(false); let loading = $state(false);
let error = $state<string | null>(null); let error = $state<string | null>(null);
@ -49,22 +50,24 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
let searchInProgress = $state(false); let searchInProgress = $state(false);
let secondOrderSearchMessage = $state<string | null>(null); let secondOrderSearchMessage = $state<string | null>(null);
let communityStatus = $state<Record<string, boolean>>({}); let communityStatus = $state<Record<string, boolean>>({});
let searchResultsCollapsed = $state(false);
userStore.subscribe((val) => (user = val)); userStore.subscribe((val) => (user = val));
function handleEventFound(newEvent: NDKEvent) { function handleEventFound(newEvent: NDKEvent) {
event = newEvent; event = newEvent;
showSidePanel = true; showSidePanel = true;
// Clear search results when showing a single event // AI-NOTE: 2025-01-24 - Preserve search results to allow navigation through them
searchResults = []; // Don't clear search results when showing a single event - this allows users to browse through results
secondOrderResults = []; // searchResults = [];
tTagResults = []; // secondOrderResults = [];
originalEventIds = new Set(); // tTagResults = [];
originalAddresses = new Set(); // originalEventIds = new Set();
searchType = null; // originalAddresses = new Set();
searchTerm = null; // searchType = null;
searchInProgress = false; // searchTerm = null;
secondOrderSearchMessage = null; // searchInProgress = false;
// secondOrderSearchMessage = null;
if (newEvent.kind === 0) { if (newEvent.kind === 0) {
try { try {
@ -255,6 +258,10 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
secondOrderSearchMessage = null; secondOrderSearchMessage = null;
} }
function toggleSearchResults() {
searchResultsCollapsed = !searchResultsCollapsed;
}
function navigateToPublication(dTag: string) { function navigateToPublication(dTag: string) {
goto(`/publications?d=${encodeURIComponent(dTag.toLowerCase())}`); goto(`/publications?d=${encodeURIComponent(dTag.toLowerCase())}`);
} }
@ -419,14 +426,24 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
<div class="main-leather flex flex-col space-y-6"> <div class="main-leather flex flex-col space-y-6">
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<Heading tag="h1" class="h-leather mb-2">Events</Heading> <Heading tag="h1" class="h-leather mb-2">Events</Heading>
{#if showSidePanel} <div class="flex items-center gap-2">
<button {#if showSidePanel && (searchResults.length > 0 || secondOrderResults.length > 0 || tTagResults.length > 0)}
class="text-sm text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200" <button
onclick={closeSidePanel} class="lg:hidden text-sm text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 border border-gray-300 dark:border-gray-600 rounded px-2 py-1"
> onclick={toggleSearchResults}
Close Details >
</button> {searchResultsCollapsed ? "Show Results" : "Hide Results"}
{/if} </button>
{/if}
{#if showSidePanel}
<button
class="text-sm text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200"
onclick={closeSidePanel}
>
Close Details
</button>
{/if}
</div>
</div> </div>
<P class="mb-3"> <P class="mb-3">
@ -457,19 +474,20 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
{#if searchResults.length > 0} {#if searchResults.length > 0}
<div class="mt-8"> <div class="mt-8">
<Heading tag="h2" class="h-leather mb-4 break-words"> <div class={showSidePanel && searchResultsCollapsed ? "lg:block hidden" : "block"}>
{#if searchType === "n"} <Heading tag="h2" class="h-leather mb-4 break-words">
Search Results for name: "{searchTerm && searchTerm.length > 50 ? searchTerm.slice(0, 50) + '...' : searchTerm || ''}" ({searchResults.length} profiles) {#if searchType === "n"}
{:else if searchType === "t"} Search Results for name: "{searchTerm && searchTerm.length > 50 ? searchTerm.slice(0, 50) + '...' : searchTerm || ''}" ({searchResults.length} profiles)
Search Results for t-tag: "{searchTerm && searchTerm.length > 50 ? searchTerm.slice(0, 50) + '...' : searchTerm || ''}" ({searchResults.length} {:else if searchType === "t"}
events) Search Results for t-tag: "{searchTerm && searchTerm.length > 50 ? searchTerm.slice(0, 50) + '...' : searchTerm || ''}" ({searchResults.length}
{:else} events)
Search Results for d-tag: "{(() => { {:else}
const term = searchTerm || dTagValue?.toLowerCase() || ''; Search Results for d-tag: "{(() => {
return term.length > 50 ? term.slice(0, 50) + '...' : term; const term = searchTerm || dTagValue?.toLowerCase() || '';
})()}" ({searchResults.length} events) return term.length > 50 ? term.slice(0, 50) + '...' : term;
{/if} })()}" ({searchResults.length} events)
</Heading> {/if}
</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)} {@const profileData = parseProfileContent(result)}
@ -599,10 +617,11 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
<div <div
class="text-sm text-gray-800 dark:text-gray-200 mt-1 line-clamp-2 break-words" 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 > {#await ((result.kind === 6 || result.kind === 16) ? parseRepostContent(result.content) : parseContent(result.content)) then parsedContent}
200 {@html parsedContent.slice(0, 200)}{parsedContent.length > 200 ? "..." : ""}
? "..." {:catch}
: ""} {result.content.slice(0, 200)}{result.content.length > 200 ? "..." : ""}
{/await}
</div> </div>
{/if} {/if}
{/if} {/if}
@ -610,15 +629,17 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
</button> </button>
{/each} {/each}
</div> </div>
</div>
</div> </div>
{/if} {/if}
{#if secondOrderResults.length > 0} {#if secondOrderResults.length > 0}
<div class="mt-8"> <div class="mt-8">
<Heading tag="h2" class="h-leather mb-4"> <div class={showSidePanel && searchResultsCollapsed ? "lg:block hidden" : "block"}>
Second-Order Events (References, Replies, Quotes) ({secondOrderResults.length} <Heading tag="h2" class="h-leather mb-4">
events) Second-Order Events (References, Replies, Quotes) ({secondOrderResults.length}
</Heading> events)
</Heading>
{#if (searchType === "n" || searchType === "d") && secondOrderResults.length === 100} {#if (searchType === "n" || searchType === "d") && secondOrderResults.length === 100}
<P class="mb-4 text-sm text-gray-600 dark:text-gray-400"> <P class="mb-4 text-sm text-gray-600 dark:text-gray-400">
Showing the 100 newest events. More results may be available. Showing the 100 newest events. More results may be available.
@ -763,10 +784,11 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
<div <div
class="text-sm text-gray-800 dark:text-gray-200 mt-1 line-clamp-2 break-words" 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 > {#await ((result.kind === 6 || result.kind === 16) ? parseRepostContent(result.content) : parseContent(result.content)) then parsedContent}
200 {@html parsedContent.slice(0, 200)}{parsedContent.length > 200 ? "..." : ""}
? "..." {:catch}
: ""} {result.content.slice(0, 200)}{result.content.length > 200 ? "..." : ""}
{/await}
</div> </div>
{/if} {/if}
{/if} {/if}
@ -774,15 +796,17 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
</button> </button>
{/each} {/each}
</div> </div>
</div>
</div> </div>
{/if} {/if}
{#if tTagResults.length > 0} {#if tTagResults.length > 0}
<div class="mt-8"> <div class="mt-8">
<Heading tag="h2" class="h-leather mb-4"> <div class={showSidePanel && searchResultsCollapsed ? "lg:block hidden" : "block"}>
Search Results for t-tag: "{searchTerm || <Heading tag="h2" class="h-leather mb-4">
dTagValue?.toLowerCase()}" ({tTagResults.length} events) Search Results for t-tag: "{searchTerm ||
</Heading> dTagValue?.toLowerCase()}" ({tTagResults.length} events)
</Heading>
<P class="mb-4 text-sm text-gray-600 dark:text-gray-400"> <P class="mb-4 text-sm text-gray-600 dark:text-gray-400">
Events that are tagged with the t-tag. Events that are tagged with the t-tag.
</P> </P>
@ -914,10 +938,11 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
<div <div
class="text-sm text-gray-800 dark:text-gray-200 mt-1 line-clamp-2 break-words" 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 > {#await ((result.kind === 6 || result.kind === 16) ? parseRepostContent(result.content) : parseContent(result.content)) then parsedContent}
200 {@html parsedContent.slice(0, 200)}{parsedContent.length > 200 ? "..." : ""}
? "..." {:catch}
: ""} {result.content.slice(0, 200)}{result.content.length > 200 ? "..." : ""}
{/await}
</div> </div>
{/if} {/if}
{/if} {/if}
@ -925,6 +950,7 @@ import CommentViewer from "$lib/components/CommentViewer.svelte";
</button> </button>
{/each} {/each}
</div> </div>
</div>
</div> </div>
{/if} {/if}

Loading…
Cancel
Save