|
|
|
|
@ -27,7 +27,7 @@
@@ -27,7 +27,7 @@
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let { post, fullView = false, preloadedReactions, parentEvent: providedParentEvent, quotedEvent: providedQuotedEvent }: Props = $props(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check if this event is bookmarked (async, so we use state) |
|
|
|
|
// Only check if user is logged in |
|
|
|
|
let bookmarked = $state(false); |
|
|
|
|
@ -93,7 +93,7 @@
@@ -93,7 +93,7 @@
|
|
|
|
|
if (isLoggedIn) { |
|
|
|
|
isBookmarked(post.id).then(b => { |
|
|
|
|
if (!cancelled) { |
|
|
|
|
bookmarked = b; |
|
|
|
|
bookmarked = b; |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} else { |
|
|
|
|
@ -462,8 +462,8 @@
@@ -462,8 +462,8 @@
|
|
|
|
|
const urlWithoutProtocol = normalized.replace(/^https?:\/\//i, ''); |
|
|
|
|
if (content.includes(normalized.toLowerCase()) || content.includes(urlWithoutProtocol.toLowerCase())) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Check if URL appears in markdown image syntax  |
|
|
|
|
const markdownImageRegex = /!\[.*?\]\((.*?)\)/gi; |
|
|
|
|
let match; |
|
|
|
|
@ -471,7 +471,7 @@
@@ -471,7 +471,7 @@
|
|
|
|
|
const markdownUrl = normalizeUrl(match[1]); |
|
|
|
|
if (markdownUrl === normalized) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Check if URL appears in HTML img/video/audio tags |
|
|
|
|
@ -480,7 +480,7 @@
@@ -480,7 +480,7 @@
|
|
|
|
|
const htmlUrl = normalizeUrl(match[2]); |
|
|
|
|
if (htmlUrl === normalized) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
@ -493,14 +493,14 @@
@@ -493,14 +493,14 @@
|
|
|
|
|
} catch { |
|
|
|
|
return url; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Extract media URLs from event tags (image, imeta, file) - for feed view only |
|
|
|
|
// Excludes URLs that are already in the content |
|
|
|
|
function getMediaUrls(): string[] { |
|
|
|
|
const urls: string[] = []; |
|
|
|
|
const seen = new Set<string>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 1. Image tag (NIP-23) |
|
|
|
|
const imageTag = post.tags.find((t) => t[0] === 'image'); |
|
|
|
|
if (imageTag && imageTag[1]) { |
|
|
|
|
@ -509,7 +509,7 @@
@@ -509,7 +509,7 @@
|
|
|
|
|
urls.push(imageTag[1]); |
|
|
|
|
seen.add(normalized); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 2. imeta tags (NIP-92) |
|
|
|
|
for (const tag of post.tags) { |
|
|
|
|
@ -536,7 +536,7 @@
@@ -536,7 +536,7 @@
|
|
|
|
|
if (!seen.has(normalized) && !isUrlInContent(tag[1])) { |
|
|
|
|
urls.push(tag[1]); |
|
|
|
|
seen.add(normalized); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -590,11 +590,11 @@
@@ -590,11 +590,11 @@
|
|
|
|
|
|
|
|
|
|
<MetadataCard event={post} hideTitle={true} hideImageIfInMedia={true} /> |
|
|
|
|
|
|
|
|
|
{@const title = getTitle()} |
|
|
|
|
{#if title && title !== 'Untitled'} |
|
|
|
|
{@const title = getTitle()} |
|
|
|
|
{#if title && title !== 'Untitled'} |
|
|
|
|
<h2 class="post-title font-bold mb-4 text-fog-text dark:text-fog-dark-text" style="font-size: 1.5em;"> |
|
|
|
|
{title} |
|
|
|
|
</h2> |
|
|
|
|
{title} |
|
|
|
|
</h2> |
|
|
|
|
{/if} |
|
|
|
|
|
|
|
|
|
<div class="post-header flex flex-col gap-2 mb-2"> |
|
|
|
|
@ -608,21 +608,21 @@
@@ -608,21 +608,21 @@
|
|
|
|
|
</div> |
|
|
|
|
<div class="flex items-center gap-2 flex-nowrap"> |
|
|
|
|
<div class="flex-shrink-1 min-w-0"> |
|
|
|
|
<ProfileBadge pubkey={post.pubkey} /> |
|
|
|
|
<ProfileBadge pubkey={post.pubkey} /> |
|
|
|
|
</div> |
|
|
|
|
<span class="text-fog-text-light dark:text-fog-dark-text-light flex-shrink-0 whitespace-nowrap" style="font-size: 0.75em;">{getRelativeTime()}</span> |
|
|
|
|
{#if getClientName()} |
|
|
|
|
{#if getClientName()} |
|
|
|
|
<span class="text-fog-text-light dark:text-fog-dark-text-light flex-shrink-0" style="font-size: 0.75em;">via {getClientName()}</span> |
|
|
|
|
{/if} |
|
|
|
|
{#if post.kind === KIND.DISCUSSION_THREAD} |
|
|
|
|
{/if} |
|
|
|
|
{#if post.kind === KIND.DISCUSSION_THREAD} |
|
|
|
|
{@const topics = getTopics()} |
|
|
|
|
{#if topics.length === 0} |
|
|
|
|
{#if topics.length === 0} |
|
|
|
|
<a href="/topics/General" class="topic-badge px-2 py-0.5 rounded bg-fog-border dark:bg-fog-dark-border text-fog-text-light dark:text-fog-dark-text-light hover:underline" style="font-size: 0.75em;">General</a> |
|
|
|
|
{:else} |
|
|
|
|
{#each topics.slice(0, 3) as topic} |
|
|
|
|
{:else} |
|
|
|
|
{#each topics.slice(0, 3) as topic} |
|
|
|
|
<a href="/topics/{topic}" class="topic-badge px-2 py-0.5 rounded bg-fog-border dark:bg-fog-dark-border text-fog-text-light dark:text-fog-dark-text-light hover:underline" style="font-size: 0.75em;">{topic}</a> |
|
|
|
|
{/each} |
|
|
|
|
{/if} |
|
|
|
|
{/each} |
|
|
|
|
{/if} |
|
|
|
|
{/if} |
|
|
|
|
</div> |
|
|
|
|
<hr class="post-header-divider" /> |
|
|
|
|
@ -636,7 +636,7 @@
@@ -636,7 +636,7 @@
|
|
|
|
|
<div class="post-actions flex flex-wrap items-center gap-2 sm:gap-4"> |
|
|
|
|
<FeedReactionButtons event={post} preloadedReactions={preloadedReactions} /> |
|
|
|
|
</div> |
|
|
|
|
{:else} |
|
|
|
|
{:else} |
|
|
|
|
<!-- Feed view: plaintext only, no profile pics, media as URLs --> |
|
|
|
|
<div class="post-header flex flex-col gap-2 mb-2"> |
|
|
|
|
<div class="flex items-center gap-2 flex-nowrap"> |
|
|
|
|
@ -651,7 +651,7 @@
@@ -651,7 +651,7 @@
|
|
|
|
|
{@const topics = getTopics()} |
|
|
|
|
{#if topics.length === 0} |
|
|
|
|
<a href="/topics/General" class="topic-badge px-2 py-0.5 rounded bg-fog-border dark:bg-fog-dark-border text-fog-text-light dark:text-fog-dark-text-light hover:underline" style="font-size: 0.75em;">General</a> |
|
|
|
|
{:else} |
|
|
|
|
{:else} |
|
|
|
|
{#each topics.slice(0, 3) as topic} |
|
|
|
|
<a href="/topics/{topic}" class="topic-badge px-2 py-0.5 rounded bg-fog-border dark:bg-fog-dark-border text-fog-text-light dark:text-fog-dark-text-light hover:underline" style="font-size: 0.75em;">{topic}</a> |
|
|
|
|
{/each} |
|
|
|
|
@ -738,22 +738,22 @@
@@ -738,22 +738,22 @@
|
|
|
|
|
{url} |
|
|
|
|
</button> |
|
|
|
|
{/each} |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
{/if} |
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
{#if !fullView && shouldCollapse} |
|
|
|
|
<div class="show-more-container"> |
|
|
|
|
<button |
|
|
|
|
onclick={toggleExpand} |
|
|
|
|
<button |
|
|
|
|
onclick={toggleExpand} |
|
|
|
|
class="show-more-btn text-fog-accent dark:text-fog-dark-accent hover:underline" |
|
|
|
|
style="font-size: 0.875em;" |
|
|
|
|
> |
|
|
|
|
> |
|
|
|
|
{isExpanded ? 'Show Less' : 'Show More'} |
|
|
|
|
</button> |
|
|
|
|
</button> |
|
|
|
|
</div> |
|
|
|
|
{/if} |
|
|
|
|
|
|
|
|
|
{/if} |
|
|
|
|
|
|
|
|
|
{#if !fullView} |
|
|
|
|
<div class="feed-card-footer flex items-center justify-between"> |
|
|
|
|
<div class="feed-card-actions flex items-center gap-2"> |
|
|
|
|
@ -771,10 +771,10 @@
@@ -771,10 +771,10 @@
|
|
|
|
|
{/if} |
|
|
|
|
|
|
|
|
|
{#if fullView} |
|
|
|
|
<div class="kind-badge"> |
|
|
|
|
<span class="kind-number">{getKindInfo(post.kind).number}</span> |
|
|
|
|
<span class="kind-description">{getKindInfo(post.kind).description}</span> |
|
|
|
|
</div> |
|
|
|
|
<div class="kind-badge"> |
|
|
|
|
<span class="kind-number">{getKindInfo(post.kind).number}</span> |
|
|
|
|
<span class="kind-description">{getKindInfo(post.kind).description}</span> |
|
|
|
|
</div> |
|
|
|
|
{/if} |
|
|
|
|
</article> |
|
|
|
|
|
|
|
|
|
|