Browse Source

bookmark paging

master
Silberengel 1 month ago
parent
commit
bbe4467c00
  1. 16
      src/lib/components/content/MediaAttachments.svelte
  2. 2
      src/lib/components/content/ReplyContext.svelte
  3. 2
      src/lib/components/layout/Header.svelte
  4. 6
      src/lib/components/layout/ProfileBadge.svelte
  5. 20
      src/lib/modules/discussions/DiscussionCard.svelte
  6. 20
      src/lib/modules/feed/FeedPage.svelte
  7. 66
      src/lib/modules/feed/FeedPost.svelte
  8. 10
      src/lib/modules/feed/ThreadDrawer.svelte
  9. 24
      src/lib/modules/profiles/ProfilePage.svelte
  10. 2
      src/lib/services/nostr/event-hierarchy.ts
  11. 2
      src/lib/services/nostr/nip30-emoji.ts
  12. 2
      src/routes/repos/[naddr]/+page.svelte
  13. 10
      src/routes/rss/+page.svelte
  14. 4
      src/routes/topics/[name]/+page.svelte

16
src/lib/components/content/MediaAttachments.svelte

@ -216,8 +216,8 @@
preload="metadata" preload="metadata"
class="max-w-full rounded" class="max-w-full rounded"
style="max-height: 500px;" style="max-height: 500px;"
autoplay={false} autoplay={false}
muted={false} muted={false}
> >
<track kind="captions" /> <track kind="captions" />
Your browser does not support the video tag. Your browser does not support the video tag.
@ -225,13 +225,13 @@
</div> </div>
{:else if item.type === 'audio'} {:else if item.type === 'audio'}
<div class="media-item"> <div class="media-item">
<audio <audio
src={item.url} src={item.url}
controls controls
preload="metadata" preload="metadata"
class="w-full" class="w-full"
autoplay={false} autoplay={false}
> >
Your browser does not support the audio tag. Your browser does not support the audio tag.
</audio> </audio>
</div> </div>

2
src/lib/components/content/ReplyContext.svelte

@ -66,7 +66,7 @@
} finally { } finally {
// Only clear loading state if this is still the current load // Only clear loading state if this is still the current load
if (lastLoadAttemptId === eventId) { if (lastLoadAttemptId === eventId) {
loadingParent = false; loadingParent = false;
} }
} }
} }

2
src/lib/components/layout/Header.svelte

@ -129,7 +129,7 @@
@media (min-width: 640px) { @media (min-width: 640px) {
.nav-brand { .nav-brand {
font-size: 1rem; font-size: 1rem;
} }
} }
/* Ensure navigation items don't overflow */ /* Ensure navigation items don't overflow */

6
src/lib/components/layout/ProfileBadge.svelte

@ -59,9 +59,9 @@
const p = await fetchProfile(currentPubkey); const p = await fetchProfile(currentPubkey);
// Only update if pubkey hasn't changed during load // Only update if pubkey hasn't changed during load
if (pubkey === currentPubkey) { if (pubkey === currentPubkey) {
if (p) { if (p) {
profile = p; profile = p;
} }
lastLoadedPubkey = currentPubkey; lastLoadedPubkey = currentPubkey;
} }
} finally { } finally {

20
src/lib/modules/discussions/DiscussionCard.svelte

@ -204,7 +204,7 @@
<article class="thread-card bg-fog-post dark:bg-fog-dark-post border border-fog-border dark:border-fog-dark-border p-4 mb-4 shadow-sm dark:shadow-lg"> <article class="thread-card bg-fog-post dark:bg-fog-dark-post border border-fog-border dark:border-fog-dark-border p-4 mb-4 shadow-sm dark:shadow-lg">
{#if !fullView} {#if !fullView}
<a href="/event/{thread.id}" class="card-link"> <a href="/event/{thread.id}" class="card-link">
<div class="card-content" class:expanded={expanded} bind:this={contentElement}> <div class="card-content" class:expanded={expanded} bind:this={contentElement}>
<div class="flex justify-between items-start mb-2"> <div class="flex justify-between items-start mb-2">
<h3 class="font-semibold text-fog-text dark:text-fog-dark-text"> <h3 class="font-semibold text-fog-text dark:text-fog-dark-text">
@ -302,15 +302,15 @@
/> />
{/if} {/if}
{#if !fullView} {#if !fullView}
{#if loadingStats} {#if loadingStats}
<span class="text-fog-text-light dark:text-fog-dark-text-light">Loading stats...</span> <span class="text-fog-text-light dark:text-fog-dark-text-light">Loading stats...</span>
{:else} {:else}
<span class="font-medium vote-comment-spacing">{commentCount} {commentCount === 1 ? 'comment' : 'comments'}</span> <span class="font-medium vote-comment-spacing">{commentCount} {commentCount === 1 ? 'comment' : 'comments'}</span>
{#if latestResponseTime} {#if latestResponseTime}
<span class="text-fog-text-light dark:text-fog-dark-text-light">Last: {getLatestResponseTime()}</span> <span class="text-fog-text-light dark:text-fog-dark-text-light">Last: {getLatestResponseTime()}</span>
{/if} {/if}
{#if zapCount > 0} {#if zapCount > 0}
<span class="font-medium">{zapTotal.toLocaleString()} sats ({zapCount})</span> <span class="font-medium">{zapTotal.toLocaleString()} sats ({zapCount})</span>
{/if} {/if}
{/if} {/if}
{:else} {:else}

20
src/lib/modules/feed/FeedPage.svelte

@ -67,9 +67,9 @@
filters, filters,
relays, relays,
{ {
relayFirst: true, relayFirst: true,
useCache: true, useCache: true,
cacheResults: true, cacheResults: true,
timeout: config.standardTimeout timeout: config.standardTimeout
} }
); );
@ -89,7 +89,7 @@
const eventMap = new Map(events.map(e => [e.id, e])); const eventMap = new Map(events.map(e => [e.id, e]));
for (const event of filtered) { for (const event of filtered) {
eventMap.set(event.id, event); eventMap.set(event.id, event);
} }
const sorted = Array.from(eventMap.values()).sort((a, b) => b.created_at - a.created_at); const sorted = Array.from(eventMap.values()).sort((a, b) => b.created_at - a.created_at);
events = sorted; events = sorted;
@ -115,9 +115,9 @@
const feedKinds = getFeedKinds().filter(k => k !== KIND.DISCUSSION_THREAD); const feedKinds = getFeedKinds().filter(k => k !== KIND.DISCUSSION_THREAD);
const cached = await getRecentFeedEvents(feedKinds, 15 * 60 * 1000, config.feedLimit); const cached = await getRecentFeedEvents(feedKinds, 15 * 60 * 1000, config.feedLimit);
const filtered = cached.filter(e => const filtered = cached.filter(e =>
e.kind !== KIND.DISCUSSION_THREAD && e.kind !== KIND.DISCUSSION_THREAD &&
getKindInfo(e.kind).showInFeed === true getKindInfo(e.kind).showInFeed === true
); );
if (filtered.length > 0 && isMounted) { if (filtered.length > 0 && isMounted) {
const unique = Array.from(new Map(filtered.map(e => [e.id, e])).values()); const unique = Array.from(new Map(filtered.map(e => [e.id, e])).values());
@ -136,8 +136,8 @@
relayError = `Relay ${singleRelay} is unavailable.`; relayError = `Relay ${singleRelay} is unavailable.`;
loading = false; loading = false;
return; return;
}
} }
}
const feedKinds = getFeedKinds().filter(k => k !== KIND.DISCUSSION_THREAD); const feedKinds = getFeedKinds().filter(k => k !== KIND.DISCUSSION_THREAD);
const filters = feedKinds.map(k => ({ kinds: [k], limit: config.feedLimit })); const filters = feedKinds.map(k => ({ kinds: [k], limit: config.feedLimit }));
@ -168,7 +168,7 @@
const eventMap = new Map(events.map(e => [e.id, e])); const eventMap = new Map(events.map(e => [e.id, e]));
for (const event of filtered) { for (const event of filtered) {
eventMap.set(event.id, event); eventMap.set(event.id, event);
} }
const sorted = Array.from(eventMap.values()).sort((a, b) => b.created_at - a.created_at); const sorted = Array.from(eventMap.values()).sort((a, b) => b.created_at - a.created_at);
events = sorted; events = sorted;
@ -206,7 +206,7 @@
const eventIds = new Set([...events.map(e => e.id), ...waitingRoomEvents.map(e => e.id)]); const eventIds = new Set([...events.map(e => e.id), ...waitingRoomEvents.map(e => e.id)]);
if (!eventIds.has(event.id)) { if (!eventIds.has(event.id)) {
waitingRoomEvents = [...waitingRoomEvents, event].sort((a, b) => b.created_at - a.created_at); waitingRoomEvents = [...waitingRoomEvents, event].sort((a, b) => b.created_at - a.created_at);
} }
}, },
() => {} () => {}
); );

66
src/lib/modules/feed/FeedPost.svelte

@ -93,7 +93,7 @@
if (isLoggedIn) { if (isLoggedIn) {
isBookmarked(post.id).then(b => { isBookmarked(post.id).then(b => {
if (!cancelled) { if (!cancelled) {
bookmarked = b; bookmarked = b;
} }
}); });
} else { } else {
@ -462,7 +462,7 @@
const urlWithoutProtocol = normalized.replace(/^https?:\/\//i, ''); const urlWithoutProtocol = normalized.replace(/^https?:\/\//i, '');
if (content.includes(normalized.toLowerCase()) || content.includes(urlWithoutProtocol.toLowerCase())) { if (content.includes(normalized.toLowerCase()) || content.includes(urlWithoutProtocol.toLowerCase())) {
return true; return true;
} }
// Check if URL appears in markdown image syntax ![alt](url) // Check if URL appears in markdown image syntax ![alt](url)
const markdownImageRegex = /!\[.*?\]\((.*?)\)/gi; const markdownImageRegex = /!\[.*?\]\((.*?)\)/gi;
@ -471,7 +471,7 @@
const markdownUrl = normalizeUrl(match[1]); const markdownUrl = normalizeUrl(match[1]);
if (markdownUrl === normalized) { if (markdownUrl === normalized) {
return true; return true;
} }
} }
// Check if URL appears in HTML img/video/audio tags // Check if URL appears in HTML img/video/audio tags
@ -480,7 +480,7 @@
const htmlUrl = normalizeUrl(match[2]); const htmlUrl = normalizeUrl(match[2]);
if (htmlUrl === normalized) { if (htmlUrl === normalized) {
return true; return true;
} }
} }
return false; return false;
@ -493,7 +493,7 @@
} catch { } catch {
return url; return url;
} }
} }
// Extract media URLs from event tags (image, imeta, file) - for feed view only // Extract media URLs from event tags (image, imeta, file) - for feed view only
// Excludes URLs that are already in the content // Excludes URLs that are already in the content
@ -509,7 +509,7 @@
urls.push(imageTag[1]); urls.push(imageTag[1]);
seen.add(normalized); seen.add(normalized);
} }
} }
// 2. imeta tags (NIP-92) // 2. imeta tags (NIP-92)
for (const tag of post.tags) { for (const tag of post.tags) {
@ -536,7 +536,7 @@
if (!seen.has(normalized) && !isUrlInContent(tag[1])) { if (!seen.has(normalized) && !isUrlInContent(tag[1])) {
urls.push(tag[1]); urls.push(tag[1]);
seen.add(normalized); seen.add(normalized);
} }
} }
} }
@ -590,11 +590,11 @@
<MetadataCard event={post} hideTitle={true} hideImageIfInMedia={true} /> <MetadataCard event={post} hideTitle={true} hideImageIfInMedia={true} />
{@const title = getTitle()} {@const title = getTitle()}
{#if title && title !== 'Untitled'} {#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;"> <h2 class="post-title font-bold mb-4 text-fog-text dark:text-fog-dark-text" style="font-size: 1.5em;">
{title} {title}
</h2> </h2>
{/if} {/if}
<div class="post-header flex flex-col gap-2 mb-2"> <div class="post-header flex flex-col gap-2 mb-2">
@ -608,21 +608,21 @@
</div> </div>
<div class="flex items-center gap-2 flex-nowrap"> <div class="flex items-center gap-2 flex-nowrap">
<div class="flex-shrink-1 min-w-0"> <div class="flex-shrink-1 min-w-0">
<ProfileBadge pubkey={post.pubkey} /> <ProfileBadge pubkey={post.pubkey} />
</div> </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> <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> <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}
{#if post.kind === KIND.DISCUSSION_THREAD} {#if post.kind === KIND.DISCUSSION_THREAD}
{@const topics = getTopics()} {@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> <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} {#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> <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} {/each}
{/if} {/if}
{/if} {/if}
</div> </div>
<hr class="post-header-divider" /> <hr class="post-header-divider" />
@ -636,7 +636,7 @@
<div class="post-actions flex flex-wrap items-center gap-2 sm:gap-4"> <div class="post-actions flex flex-wrap items-center gap-2 sm:gap-4">
<FeedReactionButtons event={post} preloadedReactions={preloadedReactions} /> <FeedReactionButtons event={post} preloadedReactions={preloadedReactions} />
</div> </div>
{:else} {:else}
<!-- Feed view: plaintext only, no profile pics, media as URLs --> <!-- Feed view: plaintext only, no profile pics, media as URLs -->
<div class="post-header flex flex-col gap-2 mb-2"> <div class="post-header flex flex-col gap-2 mb-2">
<div class="flex items-center gap-2 flex-nowrap"> <div class="flex items-center gap-2 flex-nowrap">
@ -651,7 +651,7 @@
{@const topics = getTopics()} {@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> <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} {#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> <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} {/each}
@ -738,21 +738,21 @@
{url} {url}
</button> </button>
{/each} {/each}
</div> </div>
{/if} {/if}
</div> </div>
{#if !fullView && shouldCollapse} {#if !fullView && shouldCollapse}
<div class="show-more-container"> <div class="show-more-container">
<button <button
onclick={toggleExpand} onclick={toggleExpand}
class="show-more-btn text-fog-accent dark:text-fog-dark-accent hover:underline" class="show-more-btn text-fog-accent dark:text-fog-dark-accent hover:underline"
style="font-size: 0.875em;" style="font-size: 0.875em;"
> >
{isExpanded ? 'Show Less' : 'Show More'} {isExpanded ? 'Show Less' : 'Show More'}
</button> </button>
</div> </div>
{/if} {/if}
{#if !fullView} {#if !fullView}
<div class="feed-card-footer flex items-center justify-between"> <div class="feed-card-footer flex items-center justify-between">
@ -771,10 +771,10 @@
{/if} {/if}
{#if fullView} {#if fullView}
<div class="kind-badge"> <div class="kind-badge">
<span class="kind-number">{getKindInfo(post.kind).number}</span> <span class="kind-number">{getKindInfo(post.kind).number}</span>
<span class="kind-description">{getKindInfo(post.kind).description}</span> <span class="kind-description">{getKindInfo(post.kind).description}</span>
</div> </div>
{/if} {/if}
</article> </article>

10
src/lib/modules/feed/ThreadDrawer.svelte

@ -206,7 +206,7 @@
const eventId = opEvent.id; const eventId = opEvent.id;
// Create abort controller to track operation lifecycle // Create abort controller to track operation lifecycle
const abortController = new AbortController(); const abortController = new AbortController();
// Load hierarchy with abort signal // Load hierarchy with abort signal
loadHierarchy(abortController.signal, eventId).catch((error) => { loadHierarchy(abortController.signal, eventId).catch((error) => {
@ -255,7 +255,7 @@
// Load reactions for the single event when drawer opens // Load reactions for the single event when drawer opens
if (opEvent) { if (opEvent) {
batchLoadReactionsForEvents([opEvent.id]); batchLoadReactionsForEvents([opEvent.id]);
} }
} }
// Don't auto-load hierarchy - only load when "View thread" is clicked // Don't auto-load hierarchy - only load when "View thread" is clicked
@ -343,7 +343,7 @@
</button> </button>
</div> </div>
{:else if hierarchyChain.length > 0} {:else if hierarchyChain.length > 0}
<!-- Display full event hierarchy (root to leaf) --> <!-- Display full event hierarchy (root to leaf) -->
{#each hierarchyChain as parentEvent, index (parentEvent.id)} {#each hierarchyChain as parentEvent, index (parentEvent.id)}
<div class="hierarchy-post"> <div class="hierarchy-post">
{#if index > 0} {#if index > 0}
@ -371,7 +371,7 @@
<!-- Display comments/replies only when full thread is loaded --> <!-- Display comments/replies only when full thread is loaded -->
{#if loadFullThread} {#if loadFullThread}
<div class="comments-section"> <div class="comments-section">
<CommentThread <CommentThread
threadId={opEvent.id} threadId={opEvent.id}
event={opEvent} event={opEvent}
@ -383,7 +383,7 @@
} }
}} }}
/> />
</div> </div>
{/if} {/if}
</div> </div>
{/if} {/if}

24
src/lib/modules/profiles/ProfilePage.svelte

@ -96,7 +96,7 @@
if (currentUserPubkey === pubkey) { if (currentUserPubkey === pubkey) {
// Reload notifications for own profile // Reload notifications for own profile
loadNotifications(pubkey); loadNotifications(pubkey);
} else { } else {
// Reload interactions for other user's profile // Reload interactions for other user's profile
loadInteractionsWithMe(pubkey, currentUserPubkey); loadInteractionsWithMe(pubkey, currentUserPubkey);
} }
@ -535,7 +535,7 @@
activeTab = 'notifications'; activeTab = 'notifications';
} else if (pins.length > 0) { } else if (pins.length > 0) {
activeTab = 'pins'; activeTab = 'pins';
} }
} else { } else {
notifications = []; notifications = [];
// Load interactions if logged in and viewing another user's profile // Load interactions if logged in and viewing another user's profile
@ -552,9 +552,9 @@
} catch (error) { } catch (error) {
// Only update state if this load wasn't aborted // Only update state if this load wasn't aborted
if (!abortController.signal.aborted && currentLoadPubkey === pubkey) { if (!abortController.signal.aborted && currentLoadPubkey === pubkey) {
console.error('Error loading profile:', error); console.error('Error loading profile:', error);
loading = false; loading = false;
profile = null; profile = null;
} }
} finally { } finally {
// Clear load tracking if this was the current load // Clear load tracking if this was the current load
@ -663,12 +663,12 @@
Pins ({pins.length}) Pins ({pins.length})
</button> </button>
{#if isOwnProfile} {#if isOwnProfile}
<button <button
onclick={() => activeTab = 'notifications'} onclick={() => activeTab = 'notifications'}
class="px-4 py-2 font-semibold {activeTab === 'notifications' ? 'border-b-2 border-fog-accent dark:border-fog-dark-accent' : ''}" class="px-4 py-2 font-semibold {activeTab === 'notifications' ? 'border-b-2 border-fog-accent dark:border-fog-dark-accent' : ''}"
> >
Notifications ({notifications.length}) Notifications ({notifications.length})
</button> </button>
{:else if currentUserPubkey && currentUserPubkey !== profilePubkey} {:else if currentUserPubkey && currentUserPubkey !== profilePubkey}
<button <button
onclick={() => activeTab = 'interactions'} onclick={() => activeTab = 'interactions'}
@ -687,8 +687,8 @@
{#each pins as pin (pin.id)} {#each pins as pin (pin.id)}
<FeedPost post={pin} /> <FeedPost post={pin} />
{/each} {/each}
</div> </div>
{/if} {/if}
{:else if activeTab === 'notifications'} {:else if activeTab === 'notifications'}
{#if notifications.length === 0} {#if notifications.length === 0}
<p class="text-fog-text-light dark:text-fog-dark-text-light">No notifications yet.</p> <p class="text-fog-text-light dark:text-fog-dark-text-light">No notifications yet.</p>
@ -707,8 +707,8 @@
{#each interactionsWithMe as interaction (interaction.id)} {#each interactionsWithMe as interaction (interaction.id)}
<FeedPost post={interaction} /> <FeedPost post={interaction} />
{/each} {/each}
</div> </div>
{/if} {/if}
{/if} {/if}
</div> </div>
{:else} {:else}

2
src/lib/services/nostr/event-hierarchy.ts

@ -46,7 +46,7 @@ export async function buildEventHierarchy(event: NostrEvent): Promise<EventHiera
const parts = aTag[1].split(':'); const parts = aTag[1].split(':');
// Validate a-tag format // Validate a-tag format
if (parts.length === 3 && /^[a-f0-9]{64}$/i.test(parts[1])) { if (parts.length === 3 && /^[a-f0-9]{64}$/i.test(parts[1])) {
return { type: 'a', value: aTag[1] }; return { type: 'a', value: aTag[1] };
} }
} }

2
src/lib/services/nostr/nip30-emoji.ts

@ -257,7 +257,7 @@ export async function resolveEmojiShortcode(
if (searchBroadly) { if (searchBroadly) {
// Only check cache if already loaded - don't trigger background fetch // Only check cache if already loaded - don't trigger background fetch
if (allEmojiPacksLoaded && shortcodeCache.has(cleanShortcode)) { if (allEmojiPacksLoaded && shortcodeCache.has(cleanShortcode)) {
return shortcodeCache.get(cleanShortcode)!; return shortcodeCache.get(cleanShortcode)!;
} }
// If packs aren't loaded yet, return null (don't fetch in background) // If packs aren't loaded yet, return null (don't fetch in background)
return null; return null;

2
src/routes/repos/[naddr]/+page.svelte

@ -218,7 +218,7 @@
// Only update if it's actually different (prevents unnecessary re-renders) // Only update if it's actually different (prevents unnecessary re-renders)
if (!repoEvent || repoEvent.id !== newRepoEvent.id) { if (!repoEvent || repoEvent.id !== newRepoEvent.id) {
repoEvent = newRepoEvent; repoEvent = newRepoEvent;
console.log('Repo event loaded:', repoEvent.id); console.log('Repo event loaded:', repoEvent.id);
} }
// Don't fetch git repo here - wait until user clicks on repository tab // Don't fetch git repo here - wait until user clicks on repository tab

10
src/routes/rss/+page.svelte

@ -131,12 +131,12 @@
if (feedsToFetch.length > 0) { if (feedsToFetch.length > 0) {
const fetchPromises = feedsToFetch.map(async (feedUrl) => { const fetchPromises = feedsToFetch.map(async (feedUrl) => {
try { try {
const items = await fetchRssFeed(feedUrl); const items = await fetchRssFeed(feedUrl);
// Cache the fetched items // Cache the fetched items
await cacheRSSFeed(feedUrl, items); await cacheRSSFeed(feedUrl, items);
return { feedUrl, items }; return { feedUrl, items };
} catch (error) { } catch (error) {
// Only log non-CORS errors to avoid console spam // Only log non-CORS errors to avoid console spam
const errorMessage = error instanceof Error ? error.message : 'Failed to fetch feed'; const errorMessage = error instanceof Error ? error.message : 'Failed to fetch feed';
if (!errorMessage.includes('CORS') && !errorMessage.includes('Cross-Origin')) { if (!errorMessage.includes('CORS') && !errorMessage.includes('Cross-Origin')) {
@ -164,7 +164,7 @@
itemMap.set(item.link, item); itemMap.set(item.link, item);
} }
// Sort by date (newest first) // Sort by date (newest first)
const combinedItems = Array.from(itemMap.values()); const combinedItems = Array.from(itemMap.values());
combinedItems.sort((a, b) => b.pubDate.getTime() - a.pubDate.getTime()); combinedItems.sort((a, b) => b.pubDate.getTime() - a.pubDate.getTime());
@ -182,7 +182,7 @@
async function fetchRssFeed(feedUrl: string): Promise<RSSItem[]> { async function fetchRssFeed(feedUrl: string): Promise<RSSItem[]> {
// Always use a CORS proxy to avoid CORS errors // Always use a CORS proxy to avoid CORS errors
// Direct fetch will fail for most RSS feeds due to CORS restrictions // Direct fetch will fail for most RSS feeds due to CORS restrictions
const proxyUrl = `https://api.allorigins.win/raw?url=${encodeURIComponent(feedUrl)}`; const proxyUrl = `https://api.allorigins.win/raw?url=${encodeURIComponent(feedUrl)}`;
const response = await fetch(proxyUrl); const response = await fetch(proxyUrl);
if (!response.ok) { if (!response.ok) {

4
src/routes/topics/[name]/+page.svelte

@ -45,8 +45,8 @@
onMount(async () => { onMount(async () => {
await nostrClient.initialize(); await nostrClient.initialize();
if (topicName) { if (topicName) {
await loadCachedTopicEvents(); await loadCachedTopicEvents();
await loadTopicEvents(); await loadTopicEvents();
} }
}); });

Loading…
Cancel
Save