5020 Commits

Author SHA1 Message Date
shakespeare.diy dcd33d5d0d Add local relay with IndexedDB event caching
Implements a local relay system that automatically caches Nostr events in IndexedDB for instant loading and offline access. Inspired by the mi project's event storage architecture.

Features:
- EventStore: IndexedDB wrapper with query support and composite indexes
- LocalRelay: Relay interface implementation backed by IndexedDB
- Automatic integration: Local relay is transparently included in all queries via NPool
- Event syncing: useEventSync hook for background event synchronization
- Settings UI: Display cache stats with export/import/clear functionality

The local relay provides zero-latency queries by responding instantly with cached events while simultaneously fetching fresh data from remote relays. All published events are automatically cached for future queries.

Cache displayed in Settings > Relays as "local://indexeddb" with event count and management controls.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 23:39:15 -06:00
shakespeare.diy 87ae7820ce Revert to 9a2b3a2: Improve relay settings UI and add nos.lol to app relays
This reverts the codebase back to the state at commit 9a2b3a2.

Reverted 1 commit(s):
- 629f163: Implement two-phase profile loading from agora

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 23:22:53 -06:00
shakespeare.diy 629f1632b1 Implement two-phase profile loading from agora
- Add PROFILE_RELAYS constant with known reliable profile relays
- Add parseAuthorEvent helper for consistent parsing
- Fast path: pool races all relays with 500ms EOSE timeout
- Slow path: race individual profile relays, resolve on first hit
- useAuthors also gets fallback for missing pubkeys
- Cache seeding continues to make individual useAuthor calls instant

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 23:16:16 -06:00
shakespeare.diy 9a2b3a2711 Improve relay settings UI and add nos.lol to app relays
- Redesigned RelayListManager with cleaner, simpler layout
- Added nos.lol to the default app relays list
- App relays now show with simple toggle switch
- User relays section with empty state when no relays configured

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 23:06:46 -06:00
shakespeare.diy a167c878ca Add separate App Relays section with toggle in settings
- Add `useAppRelays` boolean option to AppConfig
- Define APP_RELAYS constant with default app relays:
  - wss://relay.ditto.pub
  - wss://relay.primal.net
  - wss://relay.damus.io
- Update NostrProvider to use getEffectiveRelays() which merges
  app relays with user relays when useAppRelays is true
- Redesign RelayListManager with two sections:
  - App Relays: Read-only list with enable/disable toggle
  - Your Relays: User's personal NIP-65 relay list (editable)
- User relays now stored separately from app relays in config
- Default config starts with empty user relays and useAppRelays=true

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 22:58:47 -06:00
shakespeare.diy b02aa489b2 Revert to dff9ec4: Fetch geocache event from found log's a tag to display treasure name
This reverts the codebase back to the state at commit dff9ec4.

Reverted 1 commit(s):
- 21d9541: Increase timeout for parent event fetching in reply detail view

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 22:50:35 -06:00
shakespeare.diy 21d9541d55 Increase timeout for parent event fetching in reply detail view
- Add optional timeout parameter to useEvent hook (defaults to 5000ms)
- Set 30-second timeout for parent event fetch in ParentNote component
- Ensures parent events load reliably even on slow relays

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 22:49:33 -06:00
shakespeare.diy dff9ec4704 Fetch geocache event from found log's a tag to display treasure name
- Parse the `a` tag (e.g. `37516:pubkey:d-tag`) into AddrCoords
- Fetch the referenced geocache event via useAddrEvent
- Display the geocache's `name` tag as a clickable link (with chest icon)
  that navigates to the geocache's naddr detail page
- Fall back to showing the d-tag identifier if the event can't be fetched

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 20:03:28 -06:00
shakespeare.diy 204ceb0486 Add treasure headers for geocache and found log notes
- Add TreasureHeader component that renders "<chest> <name> hid a treasure"
  for geocache listings (kind 37516) and "<chest> <name> found a treasure"
  for found logs (kind 7516), styled like the existing RepostHeader
- Create FoundLogContent component for rendering kind 7516 found log events
  with geocache reference badge, verified badge, log text, and images
- Add kind 7516 detection to NoteCard and PostDetailPage content dispatch

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 20:02:00 -06:00
shakespeare.diy bb1d4f0160 Replace MapPin with chest SVG icon for Treasures
- Create ChestIcon component using the chest SVG from the treasures project
- Replace all MapPin usages for Treasures in sidebar, feed settings, treasures page, and router
- Remove unused @lucide/lab dependency (chest SVG is inlined)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 19:59:20 -06:00
shakespeare.diy c938b75887 Add sub-options for Treasures feed settings (geocaches & found logs)
- Split Treasures into two sub-kinds: Geocaches (kind 37516) and Found Logs (kind 7516)
- Add `showTreasureGeocaches`, `showTreasureFoundLogs`, `feedIncludeTreasureGeocaches`, `feedIncludeTreasureFoundLogs` to FeedSettings
- Extend ExtraKindDef with optional `subKinds` array for nested settings
- Update FeedSettingsForm to render nested sub-kind toggles under parent entries
- Update useStreamKind to accept single kind or array of kinds
- Create TreasuresPage that filters kinds based on sub-kind settings
- Add getPageKinds() utility for resolving sub-kind-aware page kinds

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 19:56:57 -06:00
shakespeare.diy 63b8301a01 Hide all scrollbars globally
Replace the show-on-hover scrollbar styles with scrollbar-width: none
and ::-webkit-scrollbar { display: none } so no visual scrollbar
ever appears on the feed or anywhere else in the app.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 19:50:17 -06:00
shakespeare.diy 7ff554f8b6 Fix hashtag clicks navigating to post detail instead of hashtag page
Add e.stopPropagation() to hashtag, nostr-link, and mention Links
inside NoteContent so clicks don't bubble up to the parent card's
onClick handler that navigates to the post detail page.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 19:47:34 -06:00
shakespeare.diy c893429749 Remove icons from Hashtag and Post Details page headers
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 19:46:08 -06:00
shakespeare.diy 7baa2d5bd3 Add icons next to page section titles
- Notifications: Bell icon
- Settings: Settings (gear) icon
- Bookmarks: Bookmark icon
- Hashtag: Hash icon
- Post Details: MessageCircle icon
- KindFeedPage: accepts icon prop (Vines=Clapperboard, Polls=BarChart3, Treasures=MapPin, Colors=Palette, Packs=PartyPopper)
- PlaceholderPage: accepts icon prop (Mutes=VolumeX)
- Wallet already had its icon

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 19:45:44 -06:00
shakespeare.diy 320d9007dc Match page title headers to compose box height (h-20), remove bottom border
Updated sticky headers on NotificationsPage, SettingsPage, BookmarksPage,
HashtagPage, KindFeedPage, WalletPage, PostDetailPage, and PlaceholderPage
to use h-20 for consistent height with the compose form, and removed the
bottom border-b from all of them.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 19:42:21 -06:00
shakespeare.diy 8bfbd23980 fix: hide action row on embedded follow packs, clean up layout
- Add compact prop to NoteCard to hide action buttons for embeds
- Pass compact to NoteCard in EmbeddedNaddr for follow pack embeds
- Remove "Starter Pack" pill from FollowPackContent
- Move member count pill to the left of the avatar stack on bottom row

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 19:36:49 -06:00
shakespeare.diy 32d0181256 refactor: use NoteCard for follow pack naddr embeds (DRY)
When an naddr for a follow pack (kind 39089/30000) appears embedded in
a note, render it using the same NoteCard component used in feeds
instead of a separate EmbeddedNaddrCard. This ensures consistent
presentation: same FollowPackContent with title, badges, description,
cover image, and avatar stack in both contexts.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 19:34:01 -06:00
shakespeare.diy 60d8a19971 feat: restore full follow pack detail view for naddr pages
When viewing a follow pack directly (naddr), show the full detail view
with member list, individual follow buttons, Follow All, and copy link.
The compact FollowPackContent is still used in NoteCard feeds.
Uses the shared PostDetailShell for consistent back button/header.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 19:31:34 -06:00
shakespeare.diy 711ed60145 refactor: unify post detail rendering for all event kinds
- Add kind-based content dispatch to PostDetailContent (same as NoteCard):
  polls, geocaches, colors, follow packs, and text notes
- Add AddrPostDetailPage that fetches by addr coords and reuses the
  same PostDetailShell/PostDetailContent
- Route all naddr identifiers through AddrPostDetailPage in NIP19Page
- Delete AddrDetailPage and FollowPackDetailPage (parallel renderers)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 19:29:01 -06:00
shakespeare.diy 769c179d1a feat: add Follow Packs to feed options
- Add kind 39089 (starter packs) to EXTRA_KINDS with sidebar/feed toggles
- Create FollowPackContent component for NoteCard rendering with title,
  description, member count badge, cover image, and avatar stack preview
- Add kind dispatch in NoteCard for 39089 and 30000
- Add /packs route using KindFeedPage
- Add PartyPopper icon in sidebar and feed settings
- Default showPacks to true so it appears in sidebar

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 19:26:10 -06:00
shakespeare.diy dfab409584 style: remove PartyPopper icon from follow pack components, use Users icon consistently
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 19:19:41 -06:00
shakespeare.diy 476e9bf177 fix: match bare NIP-19 identifiers (without nostr: prefix) in note content
The content parser regex only matched nostr:-prefixed identifiers like
`nostr:naddr1...`. Bare identifiers like `naddr1...` were rendered as
plain text. Added a \b word-boundary alternation to also match bare
npub1, note1, nprofile1, nevent1, and naddr1 identifiers, so they
render as proper embeds/mentions/links.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 19:16:24 -06:00
shakespeare.diy 4d427074d6 feat: add follow pack / starter pack rendering for kind 39089 and 30000
- Create FollowPackDetail component with member list, Follow All button,
  individual follow/unfollow per member, copy link, and pack metadata
- Route naddr identifiers for kinds 39089 (starter packs) and 30000
  (follow sets) to the dedicated follow pack view in NIP19Page
- Enhance EmbeddedNaddr to show "Starter Pack" / "Follow Set" badge
  and member count when embedding follow packs inline in notes
- Uses batch author fetching (useAuthors) for efficient member profiles
- Uses safe follow actions (useFollowActions) for individual follows
- Implements full "Follow All" with fresh kind 3 fetch from multiple
  relays before mutation (matching follow-party safety pattern)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 19:15:01 -06:00
shakespeare.diy 99fa4b2bd1 Align search bar height with Follows/Global tabs row (py-3.5)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 19:07:18 -06:00
shakespeare.diy 9a0a692ed7 Update search wrapper padding from py-4 to py-3.5
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 18:59:19 -06:00
shakespeare.diy c52b28dc60 Update search input padding from py-2.5 to py-3.5
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 18:58:23 -06:00
shakespeare.diy 2a5f97ee84 Replace mt-3 mb-2 with py-4 on search bar wrapper in left sidebar
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 18:47:56 -06:00
shakespeare.diy b197887141 Add top margin to search bar to align with feed tabs
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 18:44:58 -06:00
shakespeare.diy 887914d39f Remove vertical padding from search bar wrapper to align with feed tabs
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 18:43:16 -06:00
shakespeare.diy abce91e16e Remove y padding from logo link in left sidebar
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 18:41:01 -06:00
shakespeare.diy ed1af93e47 Update left sidebar logo size from 36 to 48x48
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 18:40:03 -06:00
shakespeare.diy d4f5b786a1 Always show mobile top nav bar on every page
- Removed hideMobileTopBar prop from MainLayout entirely
- Added STICKY_HEADER_CLASS constant in utils for consistent mobile-aware
  sticky positioning (top-10 on mobile, top-0 on desktop)
- Updated all pages to use the shared constant: Search, KindFeed (Vines),
  Hashtag, Wallet, Settings, Placeholder, NotFound, Bookmarks,
  Notifications, and Profile
- Back arrows hidden on desktop where sidebar provides navigation

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 18:38:53 -06:00
shakespeare.diy a9121b4c9b Show mobile top nav bar on Notifications and Profile pages
- Removed hideMobileTopBar prop from NotificationsPage and ProfilePage
- Adjusted sticky headers to use top-10 on mobile (below the 40px top bar)
  and top-0 on desktop (sidebar breakpoint) where the top bar is hidden
- Hid the back arrow on desktop where the sidebar provides navigation

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 18:34:22 -06:00
shakespeare.diy c770ea9af0 Make sidebar search bar a general search with people autocomplete
- Updated ProfileSearchDropdown with `enableTextSearch` prop that shows
  a combined dropdown with a "Search for ..." text option plus people results
- Pressing Enter without a profile selected navigates to /search?q=...
- Selecting a profile from autocomplete still navigates to their profile
- Updated SearchPage to read `q` param from URL and sync search state
- Changed sidebar search placeholder from "Search people..." to "Search..."

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 18:30:12 -06:00
shakespeare.diy ab1ec4503c perf: adopt Agora's feed loading optimizations
Three key changes stolen from ../agora that significantly speed up
the feed:

1. eoseTimeout: 500 on NPool — resolves relay queries as soon as any
   relay sends EOSE instead of waiting for all relays to finish.
   This was the single biggest latency improvement.

2. Batch author prefetching (useAuthors hook) — collects all unique
   pubkeys from the feed and fetches their kind-0 profiles in a
   single relay query, then seeds the individual ['author', pubkey]
   cache.  Previously each NoteCard called useAuthor independently,
   firing N separate relay queries in parallel.

3. placeholderData on useFeed and useEventStats — keeps showing
   previous data while refetching instead of flashing loading
   skeletons, eliminating visual flicker on tab switches and
   background refetches.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 18:13:42 -06:00
shakespeare.diy f2f273068b Fix ProfileRightSidebar width to match (300px)
ProfileRightSidebar still had the old w-[340px], causing the same
off-center issue on profile pages. Now matches the 300px standard.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 16:46:27 -06:00
shakespeare.diy f8e30c4de4 Fix feed centering: match sidebar widths (300px each)
Right sidebar was 340px vs left sidebar 300px, causing the feed
column to appear shifted left. Now both sidebars are 300px with
max-w-[1200px] container for symmetric centering.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 16:45:11 -06:00
shakespeare.diy c08ae32cb9 Fix alignment and sizing to match Ditto layout
- Widen left sidebar from 280px to 300px to match Ditto
- Add max-w-[1240px] and mx-auto to center the three-column layout
- Adjust compose box textarea top padding (py-2 → pt-3 pb-2) for better vertical alignment of placeholder text with avatar

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 16:43:37 -06:00
shakespeare.diy cbc02a5ec1 Add infinite scroll to profile page posts and likes
- Create useProfileFeed hook with useInfiniteQuery for paginated
  posts/replies/media on profile pages (30 events per page, with
  over-fetching for filtered tabs)
- Create useProfileLikes hook with cursor-based pagination through
  kind 7 reactions, resolving liked events per page
- Update ProfilePage to use new infinite scroll hooks with
  intersection observer sentinel and loading spinner
- Flatten and deduplicate pages in the component
- Remove old single-fetch useQuery for profile posts (was limited to 50)
- Remove old useProfileLikes inline hook

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 10:47:22 -06:00
shakespeare.diy 796807d69a Fix "No posts" flash on Follows tab before skeleton loads
Use `isPending` instead of `isLoading` for the Feed skeleton condition.
When the follow list is still loading, the feed query is disabled, which
means `isLoading` is false but `isPending` is true. This caused the
empty state ("No posts yet") to flash before the skeleton appeared.
`isPending` covers both active loading and disabled-waiting states.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 10:40:13 -06:00
shakespeare.diy 029bbc8345 Fix cramped interaction modal tabs: widen modal, tighten tab spacing
- Increased modal max-width from 420px to 460px
- Reduced tab gap, font size, and padding for breathing room
- Hide tab labels on very narrow screens (below 400px) showing icon + count only
- Shrunk active indicator width to match compact layout

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 10:37:56 -06:00
shakespeare.diy c247b17b66 Consolidate quote counts with reposts, limit emoji display to top 3
- Removed separate quote button from NoteCard and PostDetailPage action bars
- Combined repost + quote counts into a single repost number everywhere
- Removed separate "Quotes" entry from PostDetailPage stats row
- Limited reaction emoji display to top 3 in stats row (was 8)
- Quotes tab still available in InteractionsModal when opened via reposts

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 10:36:21 -06:00
shakespeare.diy ef48351510 Add quote counts to note cards, post detail page, and interactions modal
- Updated useEventStats to query for quotes via `#q` tag (NIP-18 q-tag)
- Added quote count button (blue Quote icon) on NoteCard action bar
- Added quote count to PostDetailPage stats row and action buttons
- Updated useEventInteractions to fetch quote events
- Added Quotes tab to InteractionsModal showing who quoted with content preview

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 10:34:30 -06:00
shakespeare.diy 3400e33207 Fix og-image not loading on Signal: exclude static files from SPA redirect
The catch-all /* -> /index.html redirect was serving HTML for /og-image.png,
so Signal's crawler got text/html instead of image/png. Added explicit rules
for static files (og-image.png, logo.png, robots.txt, manifest.webmanifest)
before the SPA fallback so they serve with correct content types.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 06:12:31 -06:00
shakespeare.diy c8af7c1ab4 Add OG image and social media meta tags for link previews
- Add og-image.png to public/
- Add Open Graph tags (og:title, og:description, og:image with dimensions and type, og:url, og:site_name)
- Add Twitter Card tags (summary_large_image) for X/Twitter previews
- Add meta description for general SEO
- Works with Signal, Twitter/X, Discord, Slack, iMessage, etc.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 06:09:20 -06:00
shakespeare.diy 5269054624 Add theme toggle to mobile slideout menu
Tapping cycles through Mew, Light, Black, and Pink themes.
Shows current theme name and icon, matching the drawer's existing row style.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 05:51:16 -06:00
shakespeare.diy 2fafb3eeae Restyle "Connect NWC Wallet" dialog to match compose modal design
- Use rounded-2xl, p-0 gap-0, hidden default close button pattern
- Custom header row with inline X close button
- Cleaner label-free inputs with placeholder text
- Right-aligned rounded pill "Connect" button matching "Post!" style
- Applied consistently to WalletPage dialog and WalletModal (desktop + mobile)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 05:50:12 -06:00
shakespeare.diy 5ce2a95587 Mobile menu: hide switch accounts when no other accounts, remove domain blocks, add dedicated wallet page
- Switch accounts section in MobileDrawer now only renders when otherUsers.length > 0
- Removed "Domain blocks" menu item and its /domain-blocks route
- Created WalletPage with full NWC management UI (status cards, wallet list, add/remove/activate connections)
- /wallet route now renders the dedicated page instead of a placeholder

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 05:47:54 -06:00
shakespeare.diy c1399fbc00 Fix zap button click not opening dialog on note cards
The inner button's e.stopPropagation() was preventing the click from
reaching the DialogTrigger/DrawerTrigger wrapper, so the dialog never
opened. Moved stopPropagation to the ZapDialog trigger div itself,
which lets Radix handle the click (opening the dialog) while still
preventing the card's navigation onClick from firing.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 05:43:31 -06:00