166 Commits

Author SHA1 Message Date
Gigi
be045557b8 feat: add nostrverse content and visibility filters to explore page
- Add visibility filter state and UI (mine/friends/nostrverse toggles)
- Create nostrverseService to fetch public content from the entire network
- Fetch both friends content and nostrverse content in parallel
- Apply visibility filters to both highlights and blog posts
- Filter buttons match highlight sidebar styling
- Users can now discover content beyond their friend network
- Maintains performance with sensible limits (50 posts, 100 highlights)
2025-10-14 12:09:12 +02:00
Gigi
9aff889835 fix: correct profile fetching implementation and dependencies
- Use eventStore.add() directly instead of mapEventsToStore
- Use tap() operator to process and store events as they arrive
- Add eventStore and settings to useEffect dependencies
- Fixes TypeScript and ESLint errors
2025-10-14 12:00:52 +02:00
Gigi
420df1fbdd feat: fetch and cache author profiles in explore page
- Create profileService to fetch and cache kind:0 metadata
- Fetch profiles for all blog post authors on explore page
- Store profiles in event store for immediate access
- Rebroadcast profiles to local/all relays per user settings
- Fixes 'Unknown' author names by ensuring profiles are cached
- Uses mapEventsToStore to automatically populate event store
2025-10-14 11:59:28 +02:00
Gigi
b1f79e3844 fix: resolve type errors and remove unused code
- Remove unused handleHighlightDelete function
- Fix all TypeScript type errors by using correct Highlight properties
- Use created_at instead of timestamp
- Use content instead of text
- Use urlReference instead of url
- All lint checks and type checks now pass
2025-10-14 10:54:51 +02:00
Gigi
0aba54bd23 feat(explore): add highlights tab to explore page
- Create fetchHighlightsFromAuthors function for fetching highlights from multiple contacts
- Add tab structure to Explore page (Writings and Highlights tabs)
- Update explore cache to handle both blog posts and highlights
- Add /explore/highlights route
- Keep UI consistent with /me page tab structure
- Implement pull-to-refresh for both tabs
- Add proper caching and streaming for highlights
2025-10-14 10:45:23 +02:00
Gigi
689963c041 refactor(theme): change default light theme to sepia
- Update default from paper-white to sepia for warmer reading
- Midnight remains default for dark mode
- Sepia provides warm, eye-friendly tones for light mode
2025-10-14 10:00:21 +02:00
Gigi
129aced1a2 feat(theme): add color theme variants for light and dark modes
- Add darkColorTheme: black, midnight (default), charcoal
- Add lightColorTheme: paper-white (default), sepia, ivory
- Extend UserSettings with color theme fields
- Update ThemeSettings UI to show color options
- Add CSS variables for all color theme variants
- Sepia and Ivory have warm, reading-friendly palettes
- Black offers true black for OLED screens
- All color themes sync via Nostr (NIP-78)
2025-10-14 09:39:13 +02:00
Gigi
994d834a0b feat(theme): add CSS variable tokens and theme classes
- Define semantic color tokens (--color-bg, --color-text, etc.)
- Add .theme-dark, .theme-light, .theme-system CSS classes
- Create theme.ts utility for theme application
- Add early boot theme script to prevent FOUC
- Support system preference with live updates
2025-10-14 09:11:38 +02:00
Gigi
ec423cad80 feat(services): youtubeMetaService with 7d localStorage cache and ID extraction 2025-10-13 19:27:34 +02:00
Gigi
e09be543bc feat: add caching to /me page for faster loading
- Create meCache service to store highlights, bookmarks, and read articles
- Seed Me component from cache on load to avoid empty flash
- Show small spinner while refreshing if cached data is displayed
- Update cache when highlights are deleted
- Only show full loading screen if no cached data is available
- Improves perceived performance similar to /explore page
2025-10-13 16:50:43 +02:00
Gigi
5ac0e7ed87 fix: deduplicate article events in library to prevent showing duplicates 2025-10-13 12:15:16 +02:00
Gigi
e1a3ae4b4d feat: render library articles using BlogPostCard component for consistency 2025-10-13 12:13:12 +02:00
Gigi
0d8a3576a6 feat: add tabbed layout to /me page with Highlights, Reading List, and Library tabs 2025-10-13 10:35:14 +02:00
Gigi
6c0a2439ad feat: instant mark-as-read with checkmark animation and read status checking
- Add functions to check if article/URL was already marked as read via NIP-25 reactions
- Make mark-as-read action instant with fire-and-forget publishing
- Add checkmark icon animation when marking as read
- Display read status on load by querying kind:7 (nostr events) and kind:17 (websites) reactions
- Add green styling for already-read state
- Button shows checkmark and is disabled when article is already marked as read
2025-10-13 10:17:16 +02:00
Gigi
079501337c fix: filter out invalid bookmarks without IDs
- Skip bookmarks that don't have a valid ID instead of generating temporary IDs
- Use bookmark's original created_at timestamp for added_at instead of Date.now()
- Fixes issue where first 2 bookmarks always showed 'Now' timestamp with no content
2025-10-13 08:50:29 +02:00
Gigi
b8b9f82d91 ux(explore): preserve posts across navigations; seed from cache; merge streamed + final 2025-10-12 23:30:56 +02:00
Gigi
b3fc9bb5c3 ux(bookmarks): avoid clearing list when no new events; decouple refetch from route changes 2025-10-12 23:26:18 +02:00
Gigi
496d1df404 perf(parallel): run local+remote fetches concurrently across services; stream+dedupe 2025-10-12 23:13:26 +02:00
Gigi
ea1046fe13 perf(local-first): always follow up with remote for articles and titles 2025-10-12 23:00:23 +02:00
Gigi
6d58d6e7f3 fix(highlights): ensure nostrverse fetch merges remote results after local for article/url 2025-10-12 22:58:25 +02:00
Gigi
e1420140d1 chore(lint): fix eslint and typescript issues; no rule changes 2025-10-12 22:55:46 +02:00
Gigi
484c2e0c2f refactor(highlights): split service into smaller modules; keep files <210 lines 2025-10-12 22:54:11 +02:00
Gigi
31f7d53829 perf(explore): stream contacts + early posts from local; merge remote later 2025-10-12 22:43:35 +02:00
Gigi
e3debfa5df perf(local-first): apply local-first then remote pattern across services (titles, bookmarks, highlights) 2025-10-12 22:42:24 +02:00
Gigi
a1305fba81 fix(explore): always query remote relays after local; stream merge into UI 2025-10-12 22:38:29 +02:00
Gigi
ca95d6c7f4 perf(ui): stream results to UI; show cached/local immediately (articles, highlights, explore) 2025-10-12 22:33:46 +02:00
Gigi
5513fc9850 perf(relays): local-first queries with short timeouts; fallback to remote if needed 2025-10-12 22:06:49 +02:00
Gigi
0adb8d6766 feat: add highlight deletion with confirmation dialog
- Create deletionService for NIP-09 kind:5 event deletion requests
- Add ConfirmDialog component for user confirmation before deletion
- Add subtle delete button to highlight items (trash icon)
- Only show delete button for user's own highlights
- Position delete button symmetrically opposite to relay indicator
- Add confirmation dialog to prevent accidental deletions
- Remove highlights from UI immediately after deletion request
- Style delete button with red hover color
- Add comprehensive confirmation dialog styling (danger/warning/info variants)

Implements NIP-09 Event Deletion Request.
Users can now delete their own highlights after confirming the action.
2025-10-11 08:38:22 +01:00
Gigi
6a6b8c4fad feat: add mark as read button for articles
- Create reactionService for handling kind:7 and kind:17 reactions
- Add mark as read button at the end of articles (📚 emoji)
- Use kind:7 reaction for nostr-native articles (/a/ paths)
- Use kind:17 reaction for external websites (/r/ paths)
- Pass activeAccount and currentArticle props through component tree
- Add responsive styling for mark as read button
- Button shows loading state while creating reaction
- Only visible when user is logged in

Implements NIP-25 (kind:7 reactions) and NIP-25 (kind:17 website reactions).
Users can now mark articles as read, creating a permanent record on nostr.
2025-10-11 08:34:36 +01:00
Gigi
ffe848883e feat: resolve and display article titles for naddr references
- Add articleTitleResolver service to fetch article titles from relays
- Extract naddr identifiers from markdown content
- Fetch article titles in parallel using relay pool
- Replace naddr references with actual article titles
- Fallback to identifier if title fetch fails
- Update markdown processing to be async for title resolution
- Pass relayPool through component tree to enable resolution

Example: nostr:naddr1... now shows as "My Article Title" instead of "article:identifier"

Improves readability by showing human-friendly article titles in cross-references
2025-10-11 01:47:11 +01:00
Gigi
ff09a8aba0 feat: add mobile auto-collapse setting 2025-10-10 17:00:52 +01:00
Gigi
eb4da419ae chore: update Boris pubkey for zap splits to npub19802see0gnk3vjlus0dnmfdagusqrtmsxpl5yfmkwn9uvnfnqylqduhr0x 2025-10-09 21:30:43 +01:00
Gigi
e56d28f82a chore: update highlight alt tag domain to read.withboris.com 2025-10-09 21:08:45 +01:00
Gigi
b266288b0f fix: add p tag (author tag) to highlights of nostr-native content
- Highlights now include p tag referencing original article author
- Allows authors to discover highlights of their work
- Follows NIP-84 best practices for highlight attribution
2025-10-09 18:36:20 +01:00
Gigi
2e96f93d81 refactor: simplify image caching to use Service Worker only
- Remove complex Cache API management with blob URLs and metadata
- useImageCache now simply returns the URL (Service Worker handles caching)
- imageCacheService reduced to just stats and clear functions
- Service Worker automatically caches all images on fetch
- Much simpler, DRY code that 'just works' for offline mode
- Stats now read directly from Cache API instead of localStorage metadata
2025-10-09 18:24:22 +01:00
Gigi
b20a67d4d0 fix: improve image cache resilience for offline viewing
- Clean up stale metadata when Cache API doesn't have cached data
- Handle online/offline state properly in image loading
- Show original URL when online, blob URL from cache when offline
- Prevent cache misses when browser clears Cache API on hard reload
2025-10-09 18:15:30 +01:00
Gigi
ceafe277d3 feat: add /explore route to discover blog posts from friends
- Create exploreService to fetch kind:30023 events from followed users
- Add BlogPostCard component for displaying blog post previews
- Add Explore page component with grid layout
- Add /explore route to App.tsx (not linked in navigation yet)
- Add responsive CSS styles for explore page and blog post cards
- Clicking blog post cards navigates to article view
2025-10-09 18:02:07 +01:00
Gigi
d6be6f364b refactor: migrate image cache from localStorage to Cache API
BREAKING CHANGE: Image cache now uses Cache API instead of localStorage

Benefits:
- Support for actual 210MB cache size (localStorage limited to 5-10MB)
- Store native Response objects (no base64 overhead)
- Asynchronous, non-blocking operations
- Better suited for large binary blobs like images
- Can handle hundreds of MB to several GB

Changes:
- Rewrite imageCacheService to use Cache API for image storage
- Keep metadata in localStorage for LRU tracking (small footprint)
- Update useImageCache hook to handle async Cache API
- Add blob URL cleanup to prevent memory leaks
- Update clearImageCache to async function

The cache now works as advertised and won't hit quota limits.
2025-10-09 17:48:59 +01:00
Gigi
43d5554c0c feat: change default image cache size to 210MB
Increase default cache size from 50MB to 210MB for better offline experience
2025-10-09 17:31:53 +01:00
Gigi
507288f51c feat: add image caching for offline mode
- Add imageCacheService with localStorage-based image caching and LRU eviction
- Create useImageCache hook for React components to fetch and cache images
- Integrate image caching with article service to cache cover images on load
- Add image cache settings (enable/disable, size limit) to user settings
- Update ReaderHeader to use cached images for article covers
- Update BookmarkViews (CardView, LargeView) to use cached images
- Add image cache configuration UI in OfflineModeSettings with:
  - Toggle to enable/disable image caching
  - Slider to set cache size limit (10-200 MB)
  - Display current cache stats (size and image count)
  - Clear cache button

Images are cached in localStorage for offline viewing, with a configurable
size limit (default 50MB). LRU eviction ensures cache stays within limits.
2025-10-09 17:23:31 +01:00
Gigi
aced38b147 fix(highlights): only show successfully reachable relays in flight mode
When creating highlights in flight mode (no remote connection), only show local relays in the relay indicator tooltip. Check connection status to determine which relays are actually reachable before setting publishedRelays field.
2025-10-09 16:41:45 +01:00
Gigi
82f52f73cc fix(highlights): always publish to all configured relays
Remove relay.connected filter when publishing/rebroadcasting. Now always attempts to publish to all configured relays and lets the relay pool handle connection state management. This ensures highlights are broadcast to all relays, not just those that report as connected at publish time.
2025-10-09 16:27:00 +01:00
Gigi
6a142f5163 fix(highlights): publish to all connected relays instead of just one
The highlight creation service was getting all relays from the pool without checking if they were actually connected. This caused highlights to only be published to a subset of relays (sometimes just one).

Now properly filters relays using relay.connected to ensure highlights are published to all actually connected relays when online.
2025-10-09 16:01:16 +01:00
Gigi
3c1114ad21 fix: resolve TypeScript type errors in offline sync
- Import IAccount from applesauce-accounts (not applesauce-core)
- Remove unused account parameter from syncLocalEventsToRemote
- Fix undefined vs null type mismatch in Bookmarks component
- All linter and type checks now pass cleanly
2025-10-09 15:52:34 +01:00
Gigi
2d5e48a64e perf: make highlight creation instant by non-blocking relay publish
Major UX improvement:
- Store event in EventStore FIRST (before publishing)
- Return highlight immediately (no await on relay publish)
- Publish to relays in background asynchronously
- UI now updates instantly (<50ms) instead of waiting seconds

Before:
1. Create event
2. Wait for relay publish (1-5 seconds)
3. Store in EventStore
4. Return to UI

After:
1. Create event
2. Store in EventStore
3. Return to UI immediately 
4. Publish to relays in background

Benefits:
- Instant highlight appearance in UI
- No blocking on network operations
- Better perceived performance
- Especially noticeable in flight mode with slow local relays
- Event still saved even if publishing fails
2025-10-09 14:01:55 +01:00
Gigi
be86634a65 fix: skip rebroadcasting when in flight mode
- Check actual relay connectivity before rebroadcasting
- Skip rebroadcast to all relays if no remote relays connected
- Still allows rebroadcast to local relays in flight mode
- Prevents unnecessary publish attempts to unreachable relays
- Logs: '✈️ Flight mode: skipping rebroadcast to remote relays'

This prevents the app from trying to rebroadcast fetched events to
remote relays when only local relays are connected (flight mode).
2025-10-09 14:00:57 +01:00
Gigi
a2041bd14d feat: show sync progress and hide indicator after successful sync
- Show spinning blue icon while event is syncing to remote relays
- Hide offline indicator completely after successful sync
- Add sync state tracking with listeners for real-time updates
- Track successful vs failed syncs separately
- Only clear offline flag for successfully synced events
- Blue spinner (#3b82f6) indicates active sync
- Clean UI: no indicator after sync completes

Behavior:
1. Create highlight offline → plane icon
2. Come back online → spinner replaces plane
3. Sync completes → no indicator (clean)
4. Sync fails → plane icon returns
2025-10-09 13:56:12 +01:00
Gigi
d294287c64 refactor: use applesauce EventStore for offline event management
Major improvements:
- Store highlights in EventStore immediately when created
- Query EventStore instead of local relays for offline sync
- Pass eventStore to highlight creation service and hooks
- Simplified offline sync: no more relay queries, just EventStore lookups
- More efficient and reliable offline event tracking
- Better integration with applesauce architecture

Benefits:
- Faster sync (no relay queries needed)
- More reliable (events always in EventStore)
- Cleaner code (leveraging applesauce patterns)
- Better separation of concerns
2025-10-09 13:54:47 +01:00
Gigi
95162d4423 feat: add flight mode indicator to offline-created highlights
- Add isOfflineCreated property to Highlight type
- Set flag when highlight is created in local-only mode
- Display small plane icon in highlight sidebar for offline-created highlights
- Lighter amber color (#fbbf24) to distinguish from Local badge
- Tooltip: 'Created while in flight mode'
- Visual indicator helps users track which highlights need syncing
2025-10-09 13:51:04 +01:00
Gigi
4224c989c6 fix: improve offline sync with better tracking and logging
- Track events explicitly when created in offline mode
- Mark highlights as offline-created when isLocalOnly is true
- Add extensive debug logging throughout sync process
- Increase query timeout from 5s to 10s for better reliability
- Add 2-second delay before syncing to allow relays to connect
- Log relay state transitions and event counts
- Log each event received during sync query
- Should help diagnose and fix offline sync issues
2025-10-09 13:49:35 +01:00