Commit Graph

17 Commits

Author SHA1 Message Date
Jure
4cde2fe4c7 Add quoted note inline preview (Phase 1 #3)
- fetchNoteById(eventId): fetches a single event by ID
- NoteContent: note1 and nevent1 (kind 1) references now parsed as
  "quote" segment type instead of plain mention text
- QuotePreview component: lazily fetches the referenced note, renders
  a bordered card with author avatar + name + truncated content;
  click navigates to the thread view
- Quote cards rendered as a block below the note text, consistent with
  how images/videos are handled
- naddr1 (kind 30023) and other nevent kinds still open via the
  existing article/njump.me handler

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 20:46:08 +01:00
Jure
af5e04f963 Add zap counts on notes (Phase 1 #2)
- fetchZapCount(eventId): fetches kind 9735 receipts for an event,
  parses millisat amounts from embedded zap request description tags,
  returns { count, totalSats }
- useZapCount hook: session-cached, same pattern as useReactionCount
- NoteCard: zap button shows " N sats" when total > 0, falls back
  to " zap" when no zaps yet; stats row shown for logged-out users
  displaying ♥ and  counts when non-zero

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 20:43:54 +01:00
Jure
ab7af96c45 Add long-form article reader (Phase 1 #1, NIP-23)
- fetchArticle(naddr): fetches kind 30023 by d-tag + author pubkey
- fetchAuthorArticles(pubkey): for future profile articles tab
- ArticleView: cover image, title, italic summary, author row (avatar +
  name + date), tag pills, full markdown body (DOMPurify sanitized),
  zap author button in header and footer, copy nostr: link, njump.me
  fallback on fetch error
- prose-article CSS: reader-optimised typography (15px base, 1.8 line
  height, h2 border, styled blockquote/code/pre/links/images)
- NoteContent: naddr1 clicks for kind 30023 now open ArticleView
  instead of falling through to njump.me
- openArticle(naddr) added to UI store with previousView tracking

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 20:42:13 +01:00
Jure
0f998eac92 Add Quote / Repost (NIP-18, roadmap #6)
- publishRepost: kind 6 event with stringified original event as content
  and ["e", id, "", "mention"] + ["p", pubkey] tags
- publishQuote: kind 1 note with user's text + appended nostr:nevent1...
  reference and ["q", id] + ["p", pubkey] tags
- QuoteModal: compose modal with live quoted-note preview (avatar, name,
  truncated content); Ctrl+Enter to post, Escape to close
- NoteCard: "repost" (one-click, shows "reposted ✓") and "quote" (opens
  QuoteModal) added to the actions row alongside reply/like/zap

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 18:28:59 +01:00
Jure
42fe32f584 Add mute/ignore user + anti-spam (roadmap #5, NIP-51)
- fetchMuteList / publishMuteList in nostr client (kind 10000)
- mute store: mutedPubkeys persisted to localStorage + synced to relay;
  mute/unmute publish kind 10000 best-effort; fetchMuteList merges relay
  list with local mutes on login
- fetchMuteList called after every login (nsec + pubkey)
- Feed: muted pubkeys filtered from both Global and Following tabs
- NoteCard: ⋯ context menu (appears on hover, hidden for own notes)
  with mute / unmute action; backdrop click closes menu
- ProfileView: mute / unmute button in the action row (next to follow)
- SettingsView: MuteSection lists muted accounts with name + avatar;
  hover to reveal unmute button; hidden when mute list is empty

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 18:23:30 +01:00
Jure
e3ba3dbcee Add SQLite note and profile cache (roadmap #3)
- Rust: rusqlite (bundled) with WAL mode; wrystr.db in app data dir
  - db_save_notes: upsert batch of raw event JSON, prune to 500 kind-1 notes
  - db_load_feed: return N most-recent kind-1 raws for instant startup display
  - db_save_profile / db_load_profile: cache NDKUserProfile JSON by pubkey
  - Falls back to in-memory SQLite if the on-disk open fails
- src/lib/db.ts: typed invoke wrappers; all errors silenced (cache is best-effort)
- feed store: loadCachedFeed() populates notes before relay connects;
  loadFeed() merges fresh+cached (so relay returning fewer notes doesn't
  erase cached ones), then saves fresh notes to SQLite
- useProfile: reads SQLite cache to show avatar/name instantly while
  relay request is in-flight; saves result to SQLite after relay responds
- Feed: calls loadCachedFeed() first → notes visible before relay connects

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 17:53:00 +01:00
Jure
b7853a14e2 Wire up NIP-19 / NIP-21 navigation + hashtag search from notes
- Mention clicks (nostr:npub1, nostr:nprofile) open internal ProfileView
- njump.me links intercepted: npub/nprofile decoded and opened internally,
  note/nevent/naddr fall through to browser (no reader yet)
- Hashtag clicks navigate to SearchView and auto-run the search
- openSearch(query) action added to UIStore; pendingSearch consumed on mount

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 20:47:57 +01:00
Jure
c4b14f1dd4 Fix read-only mode: hide write actions when no signer
- ComposeBox, action row (reply/like/zap), and write article button
  all gated on getNDK().signer in addition to loggedIn
- Prevents read-only (npub-only) users from seeing actions they can't use
- Found during browser testing of the login path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 19:32:06 +01:00
Jure
8fcabac450 Add zaps: NWC wallet connect + NIP-57 zap flow
- NWC client (nwc.ts): parse URI, encrypt/send kind 23194, await kind 23195 response
- Lightning store: persist NWC URI to localStorage, zap() via NDKZapper + lnPay callback
- ZapModal: amount presets (21/100/500/1000/5000 sats), custom amount, optional comment,
  paying/success/error states, prompts to Settings if no wallet connected
-  zap button on NoteCard (action row) and ProfileView (header, next to follow)
- Settings > Lightning Wallet section: paste NWC URI, connect/disconnect

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 17:45:39 +01:00
Jure
d52cfa5f75 Add network reaction counts to note cards
- fetchReactionCount (kind 7 #e filter) in nostr lib
- useReactionCount hook with module-level cache to avoid refetching
- NoteCard shows count next to like button; increments optimistically on like
- Falls back to "like"/"liked" text when count is zero or still loading

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 17:31:42 +01:00
Jure
b465ad03a3 Add editable own profile + navigation fixes
- ProfileView shows edit form when viewing own profile
- publishProfile (kind 0) added to nostr lib
- Sidebar name/avatar opens own profile
- Back button in edit mode cancels form; outside edit mode navigates back
- goBack safeguard: falls back to feed if previousView === currentView
- Fix ThreadView crash when selectedNote is null
- Tighten feed filter for base64 blobs and protocol messages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 19:19:24 +01:00
Jure
366731f9d7 Add thread view with replies
- Click note content to open thread view
- ThreadView shows root note, reply composer, and replies
- fetchReplies added to nostr lib (kind 1 #e filter)
- UI store gains openThread, goBack, previousView for navigation history
- Profile back button now returns to previous view correctly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 18:55:57 +01:00
Jure
30b5bb8d42 Add following feed + persist likes to localStorage
- Following tab in feed header (visible when logged in)
- Fetches kind 1 notes from followed pubkeys via NDK
- fetchFollows on login using NDK user.follows()
- fetchFollowFeed added to nostr lib
- Liked note IDs persisted in localStorage so likes survive refresh

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 18:52:26 +01:00
Jure
5879a640df Add profile view with clickable names/avatars
- ProfileView shows avatar, bio, nip05, website, recent notes
- Clicking any name or avatar navigates to their profile
- Add fetchUserNotes to nostr lib (kind 1 by author)
- Add openProfile action + selectedPubkey to UI store
- Back button returns to feed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 18:48:05 +01:00
Jure
5e20e5a128 Compose, reactions, replies + feed filtering
- Add ComposeBox with Ctrl+Enter shortcut and char limit
- Add reply/like actions to NoteCard with inline reply box
- Add publishNote, publishReaction, publishReply to nostr lib
- Filter bot JSON blobs from feed (content starting with { or [)
- Fix sidebar login button always visible (shrink-0 + nav overflow)
- Restore pubkey sessions from localStorage on startup
- Add CLAUDE.md with project guidance
- Add thread view and onboarding notes to AGENTS.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 18:44:04 +01:00
Jure
0b70d25712 Note rendering + login system
- Rich note content parser: clickable links, inline images, videos
- URL shortening for display, trailing punctuation cleanup
- nostr: mention parsing (npub, note, nevent, nprofile)
- Hashtag highlighting
- NIP-05 display on note cards
- Login modal with nsec (full access) and npub (read-only) modes
- User store with Zustand, NDK signer integration
- Sidebar shows logged-in user avatar/name + logout
- Login state persisted via localStorage (pubkey only, never nsec)
2026-03-08 16:53:14 +01:00
Jure
b75ccb7f46 Working feed: NDK + relay connection + live notes from Nostr
- Tailwind CSS + Zustand + NDK installed and configured
- Sidebar with feed/relays/settings navigation
- Global feed view with live notes from relays
- Profile fetching with caching and deduplication
- Relay connection with timeout handling
- Note cards with avatar, name, timestamp, content
- Dark theme, monospace, no-slop UI
- Devtools enabled for debugging
2026-03-08 14:54:04 +01:00