Commit Graph

4447 Commits

Author SHA1 Message Date
shakespeare.diy 3d6c1ad7f5 Remove pill styling and card background from preview
- Preview toggle is now plain text link without pill/border
- Remove border and background from preview area
- Clean, minimal preview that blends with compose area
- Just shows text label "Preview" / "Edit" with hover effect

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 21:27:09 -06:00
shakespeare.diy ec5a82ae4a Add post preview mode with toggle button
- Replace embed preview toggle with full post preview mode
- Preview button positioned in center of toolbar with label
- Shows "Preview" in edit mode, "Edit" in preview mode
- Preview displays how the post will actually render with NoteContent
- Renders all embeds, hashtags, and URLs as they would appear
- Mock event created for preview with current content
- Bordered preview area with subtle background
- Only shows toggle when content exists

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 21:26:15 -06:00
shakespeare.diy d9f2b9e31e Simplify embed previews with toggle button
- Remove complex inline rendering and hidden text approach
- Add simple eye/eye-off toggle button to show/hide embed previews
- Previews now render cleanly below textarea when enabled
- Toggle button only appears when embeds are detected
- Identifiers remain visible in textarea (no hiding complexity)
- Much simpler implementation that doesn't break rendering

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 21:24:21 -06:00
shakespeare.diy 29cfa05ebb Render NIP-19 embeds inline with hidden identifier text
- NIP-19 identifier strings are now hidden in the composer textarea
- Embedded cards render inline at the position of the identifier
- Cursor automatically moves to newline after pasting identifier
- Textarea uses dual-layer approach: hidden input + visual display overlay
- Each line is rendered separately to support inline embed positioning
- Link previews still render at bottom (not inline)
- X button removes both the identifier and the preview

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 21:17:24 -06:00
shakespeare.diy 2b791b9c40 Hide NIP-19 identifier strings in composer textarea
- NIP-19 identifiers (nevent1, note1, naddr1) are now hidden in the composer
- Textarea shows cleaned text via overlay while preserving actual content
- Identifiers still included in posted event content
- Visual feedback through inline embedded previews with X button
- Maintains proper caret positioning and text selection

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 21:13:28 -06:00
shakespeare.diy f519017d35 Add inline preview for raw NIP-19 identifiers in ComposeBox
- Detect raw nevent1, note1, and naddr1 identifiers without nostr: prefix
- Render inline embedded preview with X button for removal
- Maintains existing nostr: URI support
- Still stores as literal string in posted event content
- Provides instant preview feedback during composition

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 21:12:25 -06:00
shakespeare.diy 0c8e21df9b Restore desktop notification indicator to size 2.5 (10px)
- Desktop: size-2.5 (10px)
- Mobile: size-2 (8px)
- Both positioned at top-right corner of bell icon

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 21:10:13 -06:00
shakespeare.diy 8bde213188 Reduce notification indicator size back to 2 (8px)
- Keep position at top-right corner of bell icon
- Apply to both desktop and mobile nav

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 21:09:41 -06:00
shakespeare.diy 4eaa719f1d Match mobile notification indicator presentation to desktop
- Wrap icon in relative container
- Position indicator at top-right corner of bell icon
- Increase size to 2.5 (10px) to match desktop
- Remove unnecessary relative class from parent link

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 21:08:59 -06:00
shakespeare.diy 7dbb7889b7 Increase desktop notification indicator size to 2.5 (10px)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 21:08:19 -06:00
shakespeare.diy a8f0028bdf Position notification indicator over bell icon on desktop
- Wrap icon in relative container to position indicator relative to icon
- Position indicator at top-right corner of bell icon
- Matches mobile notification indicator placement

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 21:07:43 -06:00
shakespeare.diy a8a60307b3 Add signup dialog link to floating compose button login flow
- Wire up onSignupClick callback to show SignupDialog from LoginDialog
- Maintains DRY principle by reusing existing SignupDialog component
- Users can now access "Create account" link from the login modal

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 21:00:20 -06:00
shakespeare.diy e850c315ad Hide notifications when logged out and make join FAB open login modal
- Hide notification nav item in left sidebar when user is logged out
- Hide notification tab in mobile bottom nav when user is logged out
- Change floating compose button to open login dialog instead of signup when logged out

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 20:59:24 -06:00
shakespeare.diy e7e4a1fa5c Add encrypted notification tracking with visual indicators
- Add notificationsCursor to EncryptedSettings interface
- Create useNotifications hook to track read/unread status
- Update NotificationsPage to mark as read after 1 second
- Highlight new notifications with accent background and left border
- Add dot indicator to Bell icon in sidebar and mobile nav when unread
- Auto-mark notifications as read when viewing the page
- Sync notification cursor across devices via NIP-78

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 20:54:01 -06:00
shakespeare.diy fd2232f671 Treat '+' as thumbs-up in reaction notifications
- Map '+' and empty content to 👍 emoji in notifications
- Matches behavior in stats computation (useTrending.ts)
- Provides consistent reaction display across the app

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 20:47:21 -06:00
shakespeare.diy 3ce7cff7ab Remove border below post interactions header
- Remove Separator component between header and tabs
- Remove unused Separator import
- Cleaner visual separation in modal

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 20:45:08 -06:00
shakespeare.diy 3764250528 Show zap count instead of sats in post detail stats
- Add zapCount to EventStats interface
- Track number of zaps in computeStats function
- Display "X Zaps" instead of "X sats" in stats row
- Update hasStats check to use zapCount
- Matches Ditto's zap display format

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 20:43:36 -06:00
shakespeare.diy 9b569f0919 Allow zapping your own posts
- Remove check preventing users from zapping their own posts
- Update canZapAuthor logic in NoteCard, PostDetailPage, and NotificationsPage
- Users can now send zaps to themselves if they have a lightning address

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 20:41:34 -06:00
shakespeare.diy 510e7a76f2 Add multiple reply authors and emoji-only enlargement
- Show multiple reply authors like "Replying to @chad and @alex"
- Display up to 2 authors, with "and X others" for more
- Enlarge emojis when they're the only content (text-5xl)
- Detect emoji-only content (max 12 emoji chars)
- Update ReplyContext to accept array of pubkeys
- Extract all non-mention p tags for reply context
- Matches Ditto's reply author display behavior

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 20:40:54 -06:00
shakespeare.diy 3e527606a4 Fix parent post not showing images/videos in post details
- Extract images and videos from parent event content
- Render ImageGallery and VideoPlayer in ParentNote component
- Matches media rendering behavior with main post detail view
- Fixes issue where parent posts with only image content appeared blank

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 20:38:07 -06:00
shakespeare.diy fe78e08ed4 Move reply context below author row in notifications
- Position "Replying to @username" after author info, before content
- Matches feed positioning for consistent UI
- Updated ReferencedPostCard and FullNoteCard components

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 20:36:38 -06:00
shakespeare.diy 6c16a48b3c Add reply context to notifications and make it DRY with feed
- Created shared ReplyContext component for consistent reply rendering
- Updated NoteCard to use shared ReplyContext component
- Added reply detection logic to ReferencedPostCard in notifications
- Added reply context rendering for liked/reposted/zapped posts in notifications
- Removed duplicate ReplyContext implementations
- Ensures "Replying to @username" appears consistently across feed and notifications

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 20:35:25 -06:00
shakespeare.diy 69bca8eb58 Show actual emoji in reaction notifications instead of generic heart
- Display the emoji from the reaction event content in notifications
- Change action text from "liked your post" to "reacted to your post"
- Remove unused Heart icon import
- Default to ❤️ emoji if reaction content is empty

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 20:33:12 -06:00
shakespeare.diy c805c8e111 Show reply context for reposts
- Display "Replying to @user" even when post is reposted
- Both repost header and reply context are now shown together
- UI issue has been resolved

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 20:30:09 -06:00
shakespeare.diy 6f4e28c3df Move reply context above content in NoteCard
- Repositioned "Replying to @user" to appear between the header and content
- Matches Ditto's UI pattern for better visual hierarchy
- Hide reply context when post is a repost (repost takes priority)
- Added click event stop propagation to reply context link

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 20:28:58 -06:00
shakespeare.diy bc95237b7c Fix React error #300 when logging out
The error occurred because the AccountSwitcher component was returning null while the DropdownMenu was still mounted and open. This caused React to throw a minified error #300 about invalid elements.

The fix ensures the dropdown menu is closed before removing the login, preventing the component from being unmounted while still rendering its dropdown content.

Changes:
- Added controlled state for dropdown menu (isOpen/setIsOpen)
- Created handleLogout function that closes dropdown before removing login
- Used setTimeout to ensure dropdown closes before state update

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 20:12:35 -06:00
shakespeare.diy cb76fafc40 Fix quote posts being treated as replies
- Remove e tag with mention marker from quotes (was causing reply behavior)
- Only use q tag for NIP-18 quote reposts
- Automatically append nostr:nevent URI to quote content
- Quote posts now publish as standalone kind 1 events, not replies
- Reorganized imports for better readability

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 18:02:42 -06:00
shakespeare.diy 5d4ad1bfbc DRY: Extract feed utilities and fix profile reposts display
- Created shared feedUtils.ts with FeedItem type and parseRepostContent function
- Updated useFeed to import from shared utilities (removed duplication)
- Updated useProfileFeed to unwrap kind 6 reposts into FeedItems
- ProfilePage now properly displays reposts with "reposted by" header
- Fixed raw JSON issue by parsing repost content and showing original event
- Maintains same repost unwrapping logic across all feeds

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 18:01:18 -06:00
shakespeare.diy c7f4292b71 Include kind 6 reposts in profile feed
- Added kind 6 to profile feed query kinds
- Updated filterByTab to always show reposts in 'posts' tab
- Reposts now appear in user profiles alongside their posts

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 17:56:54 -06:00
shakespeare.diy ba43340e8c Fix quote posts being treated as replies
- When quotedEvent is present, don't pass event as replyTo in ComposeBox
- This ensures quote posts are published as kind 1 with q tags, not replies
- Repost (kind 6) was already working correctly

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 17:55:15 -06:00
shakespeare.diy fcc2ef606d Remove X button from quoted event embed in compose
- Quoted events from the quote button cannot be removed
- X button still available for detected embeds typed in content
- Provides clearer UX for intentional quote posts vs detected links

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 17:45:25 -06:00
shakespeare.diy 63580133c8 Add bottom margin to embeds in compose box
- Added mb-3 to embed containers for spacing before toolbar
- Ensures proper visual separation between embedded content and action buttons

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 17:44:06 -06:00
shakespeare.diy 72cf232ed5 Increase spacing between toolbar and embeds in compose box
- Changed embed margin from mt-2 to mt-4 for better visual separation
- Applies to both quoted events and detected embeds (nevent, naddr, links)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 17:40:41 -06:00
shakespeare.diy 3f19edd7a1 Implement quote posts and removable embeds in compose box
- Added quotedEvent prop to ComposeBox for quote post support
- Auto-detect and display embeds: nevent, note, naddr, and link previews
- Add remove button (X) to clear any embed from compose
- Quote posts open ReplyComposeModal with quoted event embedded
- Updated RepostMenu to open quote dialog instead of toast
- Added NIP-18 quote tags (q tag) when publishing quotes
- Embeds shown at bottom of compose with EmbeddedNote/EmbeddedNaddr/LinkPreview
- Support for manually typed nostr: URIs in compose content
- Invalidate event stats after quote to update counts

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 17:39:18 -06:00
shakespeare.diy 5b71e44875 Add repost menu with desktop popover and mobile drawer
- Created RepostMenu component with two options: Repost and Quote post
- Shows as popover on desktop (above button, aligned left)
- Shows as drawer on mobile with Close button
- Integrated into NoteCard, PostDetailPage, and NotificationsPage
- Implements kind 6 repost functionality
- Quote post shows placeholder toast (to be implemented)
- Includes proper event invalidation to update stats after repost

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 17:34:35 -06:00
shakespeare.diy 8249b4dabf Fix inconsistent number sizes in action buttons and adjust spacing
- Changed PostDetailPage action button numbers from text-xs to text-sm for consistency with ReactionButton
- Added tabular-nums class to all number spans for proper alignment
- Adjusted gap between action buttons from gap-4 to gap-5 for better spacing
- All action button numbers now consistently use text-sm tabular-nums

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 17:29:54 -06:00
shakespeare.diy 9db517c12e Increase action button icon size to 20px and reduce spacing
- Changed all action button icons from size-[18px] (18px) to size-5 (20px)
- Updated icons in NoteCard, PostDetailPage, NotificationsPage, and ReactionButton
- Reduced gap between action buttons from gap-6 to gap-4 for tighter spacing
- Icons affected: MessageCircle, Repeat2, Heart, Zap, MoreHorizontal

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 17:27:27 -06:00
shakespeare.diy 5b783ba7d5 Hide zap button for users without lightning addresses
- Created canZap() utility function to check if a user has lud16 or lud06
- Updated ZapButton component to use the new helper
- Conditionally render zap button in NoteCard when user can be zapped
- Conditionally render zap button in PostDetailPage when user can be zapped
- Conditionally render zap button in NotificationsPage ActionButtons when user can be zapped
- Updated ProfilePage to use canZap() helper for consistency
- Zap icon now only shows for users who can actually receive zaps

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 17:23:06 -06:00
shakespeare.diy 45d1f878f4 Prevent dialog and drawer overlay clicks from bubbling to parent elements
Add stopPropagation to dialog and drawer overlay/content click handlers to prevent clicks on modals from triggering click events on underlying elements like NoteCard.

This fixes the issue where clicking outside the zap modal would still register as a note click and navigate to the post detail page.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 17:19:04 -06:00
shakespeare.diy 06bcf8ad0e Fix modal close events triggering post navigation
Prevent clicks from closing modals (image gallery, zap dialog, etc.) from bubbling up to NoteCard and triggering unwanted navigation to post details.

Changes:
- Added handleCardClick in NoteCard to check if click originated from modal/dialog elements
- Updated ImageGallery to stop propagation when closing lightbox via backdrop or close button
- Enhanced event handling for download button in lightbox
- Prevents navigation when clicking on dialog overlays, drawer overlays, or modal content

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 17:11:23 -06:00
shakespeare.diy 4f2b50c189 Add fallback to use first p tag for reply context
When a note is a reply (has e tags) but all p tags are marked as mentions, we now fall back to using the first p tag for the reply context. This handles clients that strictly follow NIP-10 and mark all p tags with appropriate markers.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 16:57:30 -06:00
shakespeare.diy 603b2018cc Fix reply detection to use e tag pubkey as fallback
When a note has e tags with root/reply markers but the p tags use mention markers, we now fall back to using the pubkey from the e tag (5th element) to determine who is being replied to. This properly handles replies that follow NIP-10 strictly and mark mentions explicitly.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 16:54:38 -06:00
shakespeare.diy a288c28f0e Add rootMargin to infinite scroll trigger
Added rootMargin: '400px' to the intersection observer so it triggers 400px before reaching the bottom. This prevents missing the trigger when scrolling fast and ensures a smoother experience by pre-loading the next page.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 16:40:23 -06:00
shakespeare.diy a0be00b8b1 Auto-fetch page 2 when page 1 loads
Added back the auto-fetch effect that loads page 2 immediately after page 1 finishes loading. This ensures users always have 2 pages of content loaded, and then the infinite scroll ref at the bottom handles loading page 3 and beyond.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 16:17:14 -06:00
shakespeare.diy b95005be35 Simplify infinite scroll using skill pattern
Replaced the complex PageBoundary system with the simpler nostr-infinite-scroll skill pattern:
- Single intersection observer ref at the bottom of the feed
- Simplified deduplication logic
- Removed unnecessary pageItems mapping
- Cleaner, more maintainable code that matches proven patterns

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 16:15:22 -06:00
shakespeare.diy a297ebae0a Add rootMargin to PageBoundary intersection observer
Added rootMargin: '400px' to the useInView hook in PageBoundary to trigger loading the next page 400px before the boundary comes into view. This matches Agora's implementation and ensures smoother infinite scroll behavior.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 16:14:08 -06:00
shakespeare.diy 2bc84b9928 Fix missing useInView import
Re-added the useInView import from react-intersection-observer that was accidentally removed in the previous commit.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 16:13:01 -06:00
shakespeare.diy 51b9ab7dc0 Fix infinite scroll by placing PageBoundary at bottom of pages
The infinite scroll was broken because the PageBoundary component was placed at the top of each page but required pageIndex > 0 and pageIndex === totalPages - 1, which would never trigger correctly.

Changed:
- Moved PageBoundary to the bottom of each page instead of the top
- Removed the pageIndex > 0 condition (no longer needed)
- Updated comments to reflect the new bottom placement

Now when you scroll to the bottom of page 0, it triggers loading page 1. When you reach the bottom of page 1, it triggers page 2, and so on.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 16:12:05 -06:00
shakespeare.diy c0bf2005a7 Fix toggle race condition by tracking synced timestamp
The toggle was still reverting because the NostrSync component was
re-syncing old encrypted settings whenever the query refetched.

Root cause:
- When toggle is clicked, it updates config and saves to Nostr
- The encrypted settings query refetches the data
- NostrSync effect runs again with the refetched (old) data
- This overwrites the just-updated local config

Solution:
- Added lastSyncedTimestamp ref to track which settings we've synced
- Only sync if the remote timestamp is newer than what we've synced
- Changed updateSettings call from mutateAsync to mutate (non-blocking)
- This prevents re-syncing the same settings multiple times

The toggle now works correctly without reverting.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 16:05:30 -06:00
shakespeare.diy b8471261a8 Fix toggle state issue in RelayListManager
The app relays toggle was reverting on first click due to a race condition
between local state and the config context. The issue was caused by:

1. Local state (useAppRelays) was being synced via useEffect
2. When toggle was clicked, both local state and config were updated
3. The useEffect re-ran and reset the local state from config
4. This created a race condition causing the toggle to appear to undo itself

Solution:
- Removed local useAppRelays state entirely
- Use config.useAppRelays directly from context
- Removed the useEffect dependency on config.useAppRelays
- Now only config context manages this state, eliminating the race condition

The toggle now works correctly on first click.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-18 15:50:47 -06:00