diff --git a/src/components/Me.tsx b/src/components/Me.tsx index 604c870a..3737c097 100644 --- a/src/components/Me.tsx +++ b/src/components/Me.tsx @@ -14,7 +14,7 @@ import { highlightsController } from '../services/highlightsController' import { writingsController } from '../services/writingsController' import { fetchLinks } from '../services/linksService' import { ReadItem, readsController } from '../services/readsController' -import { BlogPostPreview, fetchBlogPostsFromAuthors } from '../services/exploreService' +import { BlogPostPreview } from '../services/exploreService' import { Bookmark, IndividualBookmark } from '../types/bookmarks' import AuthorCard from './AuthorCard' import BlogPostCard from './BlogPostCard' @@ -33,7 +33,6 @@ import { deriveLinksFromBookmarks } from '../utils/linksFromBookmarks' import { readingProgressController } from '../services/readingProgressController' import { archiveController } from '../services/archiveController' import { UserSettings } from '../services/settingsService' -import { getActiveRelayUrls } from '../services/relayManager' interface MeProps { relayPool: RelayPool @@ -131,17 +130,6 @@ const Me: React.FC = ({ } }, []) - // Background fetch to populate event store with ALL writings (non-blocking) - useEffect(() => { - if (!viewingPubkey || !relayPool || !eventStore) return - - // Fetch all writings in background without limits - const relayUrls = getActiveRelayUrls(relayPool) - - fetchBlogPostsFromAuthors(relayPool, [viewingPubkey], relayUrls, undefined, null, eventStore) - .catch(err => console.warn('⚠️ [Me] Failed to fetch writings:', err)) - }, [viewingPubkey, relayPool, eventStore, refreshTrigger]) - // Sync filter state with URL changes useEffect(() => { const normalized = urlFilter === 'emoji' ? 'archive' : urlFilter diff --git a/src/components/Profile.tsx b/src/components/Profile.tsx index 9b92c40c..17ed5e33 100644 --- a/src/components/Profile.tsx +++ b/src/components/Profile.tsx @@ -6,10 +6,8 @@ import { RelayPool } from 'applesauce-relay' import { nip19 } from 'nostr-tools' import { useNavigate } from 'react-router-dom' import { HighlightItem } from './HighlightItem' -import { BlogPostPreview, fetchBlogPostsFromAuthors } from '../services/exploreService' -import { fetchHighlights } from '../services/highlightService' +import { BlogPostPreview } from '../services/exploreService' import { KINDS } from '../config/kinds' -import { getActiveRelayUrls } from '../services/relayManager' import AuthorCard from './AuthorCard' import BlogPostCard from './BlogPostCard' import { BlogPostSkeleton, HighlightSkeleton } from './Skeletons' @@ -20,6 +18,8 @@ import { usePullToRefresh } from 'use-pull-to-refresh' import RefreshIndicator from './RefreshIndicator' import { Hooks } from 'applesauce-react' import { readingProgressController } from '../services/readingProgressController' +import { writingsController } from '../services/writingsController' +import { highlightsController } from '../services/highlightsController' interface ProfileProps { relayPool: RelayPool @@ -103,17 +103,16 @@ const Profile: React.FC = ({ }) }, [activeAccount?.pubkey, relayPool, eventStore, refreshTrigger]) - // Background fetch to populate event store (non-blocking) + // Background fetch via controllers to populate event store useEffect(() => { if (!pubkey || !relayPool || !eventStore) return - // Fetch all highlights and writings in background (no limits) - const relayUrls = getActiveRelayUrls(relayPool) - - fetchHighlights(relayPool, pubkey, undefined, undefined, false, eventStore) + // Start controllers to fetch and populate event store + // Controllers handle streaming, deduplication, and storage + highlightsController.start({ relayPool, eventStore, pubkey }) .catch(err => console.warn('⚠️ [Profile] Failed to fetch highlights:', err)) - fetchBlogPostsFromAuthors(relayPool, [pubkey], relayUrls, undefined, null, eventStore) + writingsController.start({ relayPool, eventStore, pubkey, force: refreshTrigger > 0 }) .catch(err => console.warn('⚠️ [Profile] Failed to fetch writings:', err)) }, [pubkey, relayPool, eventStore, refreshTrigger]) diff --git a/src/services/writingsController.ts b/src/services/writingsController.ts index d64c6899..fa10b148 100644 --- a/src/services/writingsController.ts +++ b/src/services/writingsController.ts @@ -10,8 +10,6 @@ const { getArticleTitle, getArticleSummary, getArticleImage, getArticlePublished type WritingsCallback = (posts: BlogPostPreview[]) => void type LoadingCallback = (loading: boolean) => void -const LAST_SYNCED_KEY = 'writings_last_synced' - /** * Shared writings controller * Manages the user's nostr-native long-form articles (kind:30023) centrally, @@ -71,34 +69,6 @@ class WritingsController { this.emitWritings(this.currentPosts) } - /** - * Get last synced timestamp for incremental loading - */ - private getLastSyncedAt(pubkey: string): number | null { - try { - const data = localStorage.getItem(LAST_SYNCED_KEY) - if (!data) return null - const parsed = JSON.parse(data) - return parsed[pubkey] || null - } catch { - return null - } - } - - /** - * Update last synced timestamp - */ - private setLastSyncedAt(pubkey: string, timestamp: number): void { - try { - const data = localStorage.getItem(LAST_SYNCED_KEY) - const parsed = data ? JSON.parse(data) : {} - parsed[pubkey] = timestamp - localStorage.setItem(LAST_SYNCED_KEY, JSON.stringify(parsed)) - } catch (err) { - console.warn('[writings] Failed to save last synced timestamp:', err) - } - } - /** * Convert NostrEvent to BlogPostPreview using applesauce Helpers */ @@ -127,6 +97,7 @@ class WritingsController { /** * Load writings for a user (kind:30023) * Streams results and stores in event store + * Always fetches ALL writings to ensure completeness */ async start(options: { relayPool: RelayPool @@ -152,15 +123,12 @@ class WritingsController { const seenIds = new Set() const uniqueByReplaceable = new Map() - // Get last synced timestamp for incremental loading - const lastSyncedAt = force ? null : this.getLastSyncedAt(pubkey) - const filter: { kinds: number[]; authors: string[]; since?: number } = { + // Fetch ALL writings without limits (no since filter) + // This ensures we get complete results for profile/my pages + const filter = { kinds: [KINDS.BlogPost], authors: [pubkey] } - if (lastSyncedAt) { - filter.since = lastSyncedAt - } const events = await queryEvents( relayPool, @@ -221,12 +189,6 @@ class WritingsController { this.lastLoadedPubkey = pubkey this.emitWritings(sorted) - // Update last synced timestamp - if (sorted.length > 0) { - const newestTimestamp = Math.max(...sorted.map(p => p.event.created_at)) - this.setLastSyncedAt(pubkey, newestTimestamp) - } - } catch (error) { console.error('[writings] ❌ Failed to load writings:', error) this.currentPosts = []