Commit Graph

4447 Commits

Author SHA1 Message Date
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 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
shakespeare.diy b04d697697 Fix ZapDialog rendering children even when zap is unavailable
When ZapDialog couldn't show (not logged in, own post, or no lightning
address), it returned null which hid the zap button entirely. Now it
renders children as-is when zapping isn't available, keeping the button
visible. Also fixed operator precedence bug in the lud06/lud16 check.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 05:42:08 -06:00
shakespeare.diy 7bcd6cdc77 Wire up ZapDialog to zap buttons on NoteCard, PostDetailPage, and NotificationsPage
The zap buttons were previously no-ops (only calling e.stopPropagation).
Now they open the ZapDialog which allows users to actually send zaps.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 05:41:12 -06:00
shakespeare.diy 1ed5bd9eac Safe follow/unfollow: fresh multi-relay fetch, preserve all tags & content
Modelled after the follow-party project's safety patterns for kind 3
follow list mutations:

- Created useFollowActions hook with safe follow() and unfollow() that
  fetch the freshest kind 3 event from multiple discovery relays + user
  relays before any mutation (prevents stale-data overwrites)
- Picks the event with the highest created_at across all relay responses
- Preserves ALL existing tags (not just p tags), preventing loss of
  non-follow metadata that other clients may store
- Preserves the content field (relay hints stored by some clients)
- Deduplicates when following (won't add duplicate p tags)
- Consolidated duplicate useFollowList definitions from ProfilePage and
  useFeed into a single shared hook exported from useFollowActions
- Removed unsafe inline follow mutation from ProfilePage

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 05:39:07 -06:00
shakespeare.diy 939c1566b7 Hide following count on profile when it's 0
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 05:33:54 -06:00
shakespeare.diy 618f8a6c82 Port star and checkerboard layouts from Espy for cleaner rendering
Star: use clip-path polygon pie slices with overlap and background
fill to eliminate anti-aliased seam lines from conic-gradient.

Checkerboard: use SVG pattern with imageRendering: pixelated for
crisp color blocks instead of CSS grid with gaps.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 05:27:36 -06:00
shakespeare.diy b0e7217e5b Revert color layouts to 180px height, render star as rectangle with conic gradient
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 05:26:15 -06:00
shakespeare.diy e5c35a266e Make color moment renders square (aspect-ratio: 1) for all layouts
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 05:25:20 -06:00
shakespeare.diy ff38bb14bc Render polls, geocaches, and color moments per their NIP specs
- PollContent (kind 1068 / NIP-88): Shows question, options with
  selectable vote UI, tallied results with percentage bars, poll type
  badge (single/multi), expiry status. Fetches kind 1018 vote events,
  deduplicates per pubkey, and allows voting when logged in.

- GeocacheContent (kind 37516 / NIP-GC): Shows cache name, type badge,
  size badge, D/T difficulty pips (1-5), description, image, and
  ROT13-decoded hint toggle. Supports verified cache indicator.

- ColorMomentContent (kind 3367 / Espy NIP): Renders color palettes
  with all 6 layout modes (horizontal, vertical, grid, star,
  checkerboard, diagonalStripes). Emoji overlay with adaptive
  light/dark text based on palette luminance. Click-to-copy hex
  swatches. Name display above palette.

- NoteCard now dispatches to the correct content component based on
  event.kind (1, 1068, 3367, 34236, 37516).

- Replaced VinesPage/useStreamVines with generic KindFeedPage/useStreamKind.
  All kind-specific routes (/vines, /polls, /treasures, /colors) use
  the same generic streaming page parameterized by kind number.

- Centralized extra-kind definitions in lib/extraKinds.ts. All feeds
  (home, search, profile, hashtag) respect feed settings for which
  extra kinds to include.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 05:24:01 -06:00
shakespeare.diy c7718f7a1f Add Feed settings tab with sidebar link and feed content toggles
- Add FeedSettings to AppConfig for persisting user preferences
- Create Settings > Feed tab with two sections:
  - Sidebar Links: toggle visibility of Vines, Polls, Treasures, Colors in nav
  - Feed Content: include these kinds in Follows/Global feeds
- Update LeftSidebar and MobileBottomNav to conditionally render nav items
- Update useFeed hook to include extra kinds (1068 Polls, 3367 Colors,
  34236 Vines, 37516 Treasures) based on feed settings
- Add routes for /polls, /treasures, /colors (placeholder pages)
- Vines enabled in sidebar by default; all others off by default

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 05:12:24 -06:00
shakespeare.diy 472fe5b0d7 Fix follows tab showing global feed while follow list is still loading
The useFeed hook's queryFn fell through to the global feed branch when
tab was 'follows' but followList hadn't loaded yet. Added an `enabled`
guard so the query doesn't run until the follow list is ready.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 05:03:59 -06:00
shakespeare.diy bb410ae6d8 Fix Bitcoin inline buttons: place directly after address instead of pushing to far right
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 05:02:39 -06:00
shakespeare.diy 698c523080 Make mobile profile fields consistent with sidebar: add favicon for URLs, copy/QR/external buttons for Bitcoin
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 05:00:59 -06:00
shakespeare.diy 1ec105f956 Pinned label UX: hover shows "Unpin?" on own pins, remove leading indent
- Added PinnedLabel component with hover state for own profile pins
- Hovering "Pinned" on your own posts changes to "Unpin?" in destructive color
- Clicking it unpins the note directly
- Other users' pinned posts show static "Pinned" label
- Removed ml-14 indent from pinned label for cleaner alignment

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 04:56:58 -06:00
shakespeare.diy efd767b80f Fix following count accent color and pinned notes relay issue
- Following icon + number now use text-primary (accent color) instead of muted
- Removed optimistic update from usePinnedNotes hook (unnecessary complexity)
- Removed staleTime from pinned notes list query so it refetches properly
- Fixed NostrProvider to invalidate ALL cached queries when relay URLs change,
  not just queries with ['nostr'] key. This ensures pinned notes (and all other
  data) gets refetched after NostrSync loads the user's actual relay list.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 04:48:08 -06:00
shakespeare.diy 3dd3511f73 Add following count, following list modal, pinned posts, and pin/unpin menu option
- Created usePinnedNotes hook for NIP-51 kind 10001 pinned notes management
- Created useProfileFollowing hook to fetch any profile's follow list and count
- Added following count (with Users icon) to the left of the post streak on profiles
- Clicking following count opens a scrollable modal listing all followed users
- Pinned posts now display at the top of the Posts tab with a pin indicator
- Added "Pin on profile" / "Unpin from profile" option to the post "..." menu
  (only visible for user's own posts)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 04:33:05 -06:00
shakespeare.diy 9507d86796 Fix lightbox close on negative space click
The backdrop click handler used a strict target === currentTarget
check, but the full-size image container div filled the entire
viewport, so clicks on empty space around the image hit the inner
div instead of the outermost one. Now clicks anywhere that aren't
on the image itself, a button, or the top bar will close the gallery.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 04:26:00 -06:00
shakespeare.diy d466e86abe Add image gallery lightbox for full-screen image viewing
Create ImageGallery component that replaces plain anchor-tag image
grids in both PostDetailPage and NoteCard with an interactive gallery:

- Clicking any image opens a full-screen cinematic lightbox overlay
- Dark blurred backdrop (bg-black/90 + backdrop-blur)
- Keyboard navigation: Arrow keys for prev/next, Escape to close
- Touch swipe support with drag tracking for mobile
- Loading spinner while high-res images load, crossfade on ready
- Prev/next chevron buttons on desktop, dot indicators on mobile
- Open-original button in top bar
- Click-outside-image to close
- "+N" overflow badge when more than 4 images exist
- Subtle hover scale on grid thumbnails
- Body scroll lock while lightbox is open
- 3-image layout uses row-span for the first image

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 04:25:01 -06:00
shakespeare.diy 9e71aea931 Fix raw image URL rendering in post details and improve skeleton
- Fix NoteContent fallback that incorrectly rendered raw media URLs as
  plain text when the entire content was a media URL. The parser would
  skip media URLs (handled by parent as embeds), but when all tokens
  were skipped, the empty-result fallback re-added the full content
  as text. Now tracks whether any regex matches occurred to distinguish
  "no matches at all" from "all matches were skipped media URLs".

- Restructure PostDetailSkeleton to match the actual PostDetailContent
  layout: proper article/replies separation (fixes double padding on
  reply skeletons), image placeholder, rounded action button circles,
  and -mx-4 action bar matching the real component.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 04:21:57 -06:00
shakespeare.diy c48ce58adc Revert to 2c7884b: Switch media type filters to imeta tag-based detection, rename labels
This reverts the codebase back to the state at commit 2c7884b.

Reverted 1 commit(s):
- 4ef4177: Query media types uniquely via relay filters (#m tag)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 04:17:33 -06:00
shakespeare.diy 4ef4177ec5 Query media types uniquely via relay filters (#m tag)
Each media type now produces its own distinct relay query that restarts
the stream, just like vines does with kind 34236:

- Images: kind 1 + #m filter for image/* MIME types
- Videos: kind 1 + #m filter for video/* MIME types
- Vines: kind 34236 (unchanged)
- All / No media: kind 1 (no #m filter)

The mediaType is now a dependency of the useEffect, so switching
between any filter restarts the stream with a fresh relay query.
Client-side imeta verification remains as a secondary check.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 04:16:55 -06:00
shakespeare.diy 2c7884bd61 Switch media type filters to imeta tag-based detection, rename labels
- Replace URL regex matching (extractImages/extractVideos) with imeta tag
  MIME type checking (hasImageImeta/hasVideoImeta) for media filtering
- Images filter now checks for imeta tags with `m image/*` MIME types
- Videos filter now checks for imeta tags with `m video/*` MIME types
- Rename "Regular videos" → "Videos"
- Rename "Short videos (Vines)" → "Vines"

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 04:14:57 -06:00
shakespeare.diy 67c6a90d64 Fix pixelated images in profile sidebar media grid
Remove explicit imageRendering inline style that was interfering with
the browser's default high-quality downscaling for thumbnails.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 04:11:42 -06:00
shakespeare.diy 9904b30807 Autoplay videos silently in profile sidebar media grid
Add autoPlay, loop, and playsInline attributes to video thumbnails
so they play on mute automatically in the media section.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 04:10:54 -06:00