Commit Graph

4447 Commits

Author SHA1 Message Date
shakespeare.diy 9796f42689 Make images/media span full card width in NoteCard
Move images and vine videos outside the avatar flex row so they
render at full card width instead of being indented by the avatar
gap. Text content stays inline with the avatar. Action buttons
are aligned with the text column.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 02:02:38 -06:00
shakespeare.diy 3b6763188d Fix ReplyContext crash when p tag value is undefined
Guard against undefined pubkey in replyTo[1] before passing to
ReplyContext, preventing genUserName from crashing on undefined input.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 02:00:24 -06:00
shakespeare.diy cbe711fa53 Fix vine video crop border: remove bg-black and use object-cover instead of object-contain
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 01:59:41 -06:00
shakespeare.diy 0ac0dc0569 DRY: merge VineCard into NoteCard, single component for all event kinds
- NoteCard now handles both kind 1 (text notes) and kind 34236 (vines)
- Extracted NoteBody (text + images) and VineBody (title + video + hashtags)
  as internal sub-components within NoteCard
- Same avatar, header, action buttons (reply, repost, react, zap, more),
  more menu, and reply modal shared across all kinds
- Addressable events (kind 30000+) use naddr encoding for navigation
- Deleted standalone VineCard.tsx — no longer needed
- Updated SearchPage and VinesPage to use NoteCard for everything

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 01:19:39 -06:00
shakespeare.diy 4f117403b0 Add Vines support: kind 34236 feed page, VineCard, search filter
- Created VinesPage with streaming feed of kind 34236 events
- Created VineCard component: renders video with thumbnail, play overlay,
  author header, title, and hashtag links
- Updated useStreamPosts: 'vines' filter switches to kind 34236 query
  (restarts stream since it's a different kind)
- Search page renders VineCard for kind 34236 events, NoteCard for kind 1
- Updated AppRouter: /vines route now uses VinesPage instead of placeholder
- Created useStreamVines hook for dedicated Vines page streaming
- Validates vine events: requires d tag, imeta with url

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 01:16:56 -06:00
shakespeare.diy 5b08d2d305 Fix filters: apply client-side without restarting the stream
- Stream effect only depends on [nostr, query] — filter toggles don't
  restart the stream or re-fetch from the relay
- Store all raw events in allEvents state (unfiltered)
- Apply includeReplies/mediaType filters via useMemo on the raw events
- Toggling a filter instantly shows/hides posts from the existing set
  instead of fetching a new batch and getting "no posts"

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 01:09:31 -06:00
shakespeare.diy 0b1abc2ca0 Make filter changes restart the stream with new results
- Added options.includeReplies and options.mediaType to effect deps
- Changing a radio button or toggle now re-fetches and re-streams
- Removed refs that were preventing filter changes from taking effect

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 01:07:25 -06:00
shakespeare.diy 63461b84a7 Use wss://relay.ditto.pub for all streaming (both default and search)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 01:05:17 -06:00
shakespeare.diy 97a5c9669e Use nostr.relay() directly for streaming instead of pool with relays option
- Call nostr.relay('wss://relay.damus.io').req() directly on a single relay
  instead of passing relays option to pool.req()
- Use refs for filter options to avoid restarting the stream on filter changes
- Minimal effect deps: only [nostr, query] trigger stream restart
- Filter changes apply via optionsRef without killing the subscription

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 01:04:06 -06:00
shakespeare.diy 9d95b10a48 Clean up debug logs from stream hook
The debug logs confirmed the stream IS working correctly:
- req() subscription opens successfully
- EOSE is received and subscription stays open
- New events arrive after EOSE
- Cleanup only happens when the preview environment reloads (replaceDocument)

Removed eoseTimeout from NPool in previous commit which was the
main fix for subscription lifecycle issues.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 01:02:21 -06:00
shakespeare.diy 67d523b260 Match bitmap/clawchat streaming pattern exactly, remove eoseTimeout
- Rewrote useStreamPosts to exactly mirror bitmap's useChatSession pattern
- Removed eoseTimeout: 200 from NPool config (not a valid NPoolOpts field,
  was potentially causing issues with subscription lifecycle)
- Uses AbortController directly (not useRef)
- fetchInitialMessages + subscribeToMessages run in parallel
- 30s timeout on initial fetch (matching bitmap)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 01:00:01 -06:00
shakespeare.diy d9d6e19ca0 Fix streaming: pass relays option directly to req() like clawchat does
- Pass `relays` array directly to nostr.req() and nostr.query()
  to bypass pool's reqRouter/eoseTimeout that was killing streams
- This is the pattern clawchat uses successfully for live subscriptions
- Default stream uses multiple relays, NIP-50 search uses relay.ditto.pub only
- Filter out future-dated events (created_at > now)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:57:41 -06:00
shakespeare.diy 99f922ea15 Fix streaming: use single relay instead of pool, filter future events
- Always use relay.ditto.pub directly for req() streaming
  (NPool's eoseTimeout: 200 was killing subscriptions prematurely)
- Filter out events with created_at in the future
- Matches clawchat pattern: fetchInitial + streamNew in parallel
- Both query() and req() go to same single relay

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:55:48 -06:00
shakespeare.diy 738b29f075 Fix streaming: run fetch + stream in parallel, use limit: 100 for req
- Match clawchat's proven streaming pattern
- fetchInitial() and streamNew() run in parallel, not sequentially
- req() uses limit: 100 (not limit: 0 which was wrong)
- Added isSubscribed guard for safe state updates
- Proper AbortError handling on both fetch and stream paths

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:53:27 -06:00
shakespeare.diy 87e1580c5a Fix streaming: query initial batch then req for live updates
- Step 1: query() with limit: 40 to fetch initial posts immediately
- Step 2: req() with limit: 0 + since: now to stream new posts live
- Fixes blank page issue (limit: 0 alone returns no stored events)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:52:09 -06:00
shakespeare.diy dcf0650eeb Fix streaming: use AsyncIterable API with AbortController, stream by default
- Rewrote useStreamPosts to use Nostrify's correct req() API which
  returns AsyncIterable, not a subscription object with .close()
- Uses AbortController to cancel streams on cleanup (fixes sub.close error)
- Streams posts by default with no search query (global kind:1 feed)
- When search query is provided, uses NIP-50 search on relay.ditto.pub
- Properly handles EOSE, CLOSED, and EVENT messages from the stream

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:51:12 -06:00
shakespeare.diy 9d3d62f7e6 Set stream subscription limit to 0 for proper real-time streaming
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:49:27 -06:00
shakespeare.diy c838a5337a Rewrite search page to match Ditto's exact layout
- Tabs (Posts/Trends/Accounts) at the very top, sticky
- Search input below tabs inside content area (icon on right)
- Search filters open by default with circled chevron toggle
- "Including replies" label + switch inline on same row
- Radio buttons laid out horizontally with flex-wrap
- "In the language:" label inline with dropdown
- Filter labels are bold (matching Ditto's white/bold style)
- Removed back arrow from header (Ditto doesn't have one)
- Accounts tab gets its own search input
- Posts stream live below filters

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:48:26 -06:00
shakespeare.diy 9bd46e93a8 Fix search page layout - move filters to collapsible section above content
- Removed broken sidebar-inside-main-column layout that squished content
- Filters now collapse/expand above the post results (matching Ditto's pattern)
- Smooth animated expand/collapse with chevron indicator
- Simplified empty states (plain text, no cards)
- Cleaned up unused useSearchPosts.ts (replaced by useStreamPosts)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:46:37 -06:00
shakespeare.diy 151746e7b2 Implement Ditto-style search page with live streaming posts
- Added Posts, Trends, and Accounts tabs
- Implemented sidebar search filters for Posts tab (replies, media type, language)
- Created useStreamPosts hook for real-time streaming subscriptions
- Search uses relay.ditto.pub for NIP-50 search functionality
- Posts stream in real-time with REQ subscription instead of static queries
- Layout matches Ditto design with filters in left sidebar on desktop
- All tabs functional with proper empty states

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:44:37 -06:00
shakespeare.diy e36bcad066 Fix profile header height to exactly match post composer (75px)
- Changed min-h from 84px to 75px to match actual ComposeBox height
- Both headers now have identical 75px height

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:37:32 -06:00
shakespeare.diy d899777776 Set profile header minimum height to match post composition
- Added min-h-[84px] to profile header
- Now matches ComposeBox total height (py-3 + textarea min-h-[44px] + py-2)
- Ensures consistent header heights across all pages

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:36:34 -06:00
shakespeare.diy b3aca61384 Match profile header height to post composition area
- Changed profile header padding from py-2 to py-3
- Now both headers have consistent 12px vertical padding
- Provides better visual alignment across pages

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:35:36 -06:00
shakespeare.diy f9635f9e29 Fix search autocomplete to show NIP-05 or npub, not both
- Show NIP-05 username if available
- Otherwise show npub with monospace font
- Removed the previous behavior of showing both username and NIP-05 separated by a dot
- Made npub slightly smaller (11px) with monospace font for better readability

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:33:29 -06:00
shakespeare.diy 0fe4ce47f1 Add "on Mew" to CTA copy for branding
- Updated CTA text to "Follow accounts you care about on Mew"
- Reinforces brand identity in the call-to-action

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:31:12 -06:00
shakespeare.diy 57df552a3f Make CTA more compact with better copy
- Reduced padding from py-6 to py-3 for more compact design
- Horizontal layout instead of vertical stacking
- Better CTA copy: "Follow accounts you care about"
- Smaller button size (sm) and simpler "Join" text
- Keeps gradient background but with better proportions
- More balanced and less overwhelming

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:30:21 -06:00
shakespeare.diy 8408bdbb9f Redesign CTA with centered, more prominent layout
- Center-aligned design for better visual balance
- Gradient background (primary/5 to primary/10) for subtle depth
- Larger, more prominent "Join Mew" button with shadow effects
- Engaging copy: "Join to unlock your personalized feed "
- Increased padding and spacing for breathing room
- More visually appealing and less boring design

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:29:31 -06:00
shakespeare.diy d879f3b363 Improve CTA styling with emphasis and better copy
- Change to "Join Mew to follow accounts & personalize your feed"
- Add emphasis with bold "Join Mew" in foreground color
- Use medium font weight for better visual hierarchy
- Make Join button smaller (sm size) for better balance
- More engaging and visually distinct CTA

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:28:30 -06:00
shakespeare.diy 46cea34669 Shorten CTA statement to be more concise
- Changed "Join Mew to personalize your feed and connect with others" to "Join to personalize your feed"
- More concise and direct call-to-action

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:27:59 -06:00
shakespeare.diy bb0161df47 Replace tabs with call-to-action for logged-out users
- Show Follow/Global tabs only when user is logged in
- Display CTA banner with Join button when logged out
- CTA message: "Join Mew to personalize your feed and connect with others"
- Join button triggers login dialog in Feed component
- Added LoginDialog and SignupDialog to Feed component
- Improves UX with clear call-to-action instead of empty tab bar

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:27:26 -06:00
shakespeare.diy 403e1a8d6b Hide Follow/Global tabs when user is logged out
- Import useCurrentUser hook to check login status
- Conditionally render tabs only when user is logged in
- Change default tab to 'global' for logged-out users
- Improves UX by showing only relevant UI elements based on auth state

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:25:54 -06:00
shakespeare.diy 3d3ac7fa19 Update LoginDialog to match borkstr format with signup link
- Added onSignupClick prop to LoginDialog interface
- Added "New here? Create account" link in LoginDialog
- Link appears after extension login button (if available) or with other options
- Matches borkstr's login dialog pattern for better UX
- Users can easily switch between login and signup flows

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:25:13 -06:00
shakespeare.diy 97afc671fd Update Join button to trigger login modal instead of signup dialog
- Changed Join button onClick handler to open LoginDialog instead of SignupDialog
- Follows the same pattern as borkstr project where Join opens the login modal
- Users can still access signup through the login modal's "More Options" section

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:24:23 -06:00
shakespeare.diy b5fd5ecd24 Fix React hooks violation in ComposeBox causing error #300 on logout
- Moved early return to after all hooks to comply with Rules of Hooks
- Changed handleLogout to async/await pattern for better error handling
- Prevents "Rendered more hooks than during the previous render" error

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:22:51 -06:00
shakespeare.diy 6bab1e4113 Simplify logo filters and add pink theme filter
- Simplified code to inline filters in className
- Light theme: dark grey filter
- Pink theme: dark pink filter with medium brightness
- Dark/Black themes: no filter (original logo)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:19:42 -06:00
shakespeare.diy 106e5df46e Update logo filters: dark grey for light theme, dark pink for pink theme
- Light theme: neutral dark grey filter (no color tint)
- Pink theme: vibrant dark pink filter with high saturation
- Improved contrast and readability on light backgrounds

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:16:56 -06:00
shakespeare.diy 3aa5c6383c Fix logo readability in light and pink themes with CSS filters
- Apply dark blue-purple filter for light theme to make logo readable
- Apply dark pink filter for pink theme to match theme and improve visibility
- Replace opacity with color filters for better contrast
- Logo remains unfiltered in dark and black themes

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:16:01 -06:00
shakespeare.diy 1004f86415 Fix theme selector and logo display issues
- Show current theme name and icon in dropdown trigger button
- Fix light theme not working (update class removal from 'mew' to 'dark')
- Remove opacity from logo in pink theme (keep full visibility)
- Logo now only uses opacity in light theme (70%)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:14:55 -06:00
shakespeare.diy 92e2aca8e8 Update logo opacity and theme selector to dropdown menu
- Use opacity (60% pink, 70% light) for MewLogo instead of color filters
- Change Mew theme icon to Cat, Black theme icon to Moon
- Convert theme selector to proper dropdown menu with chevron indicator
- Add DropdownMenuLabel and separator for better UX
- Maintain theme icons: Cat (Mew), Sun (Light), Moon (Black), Heart (Pink)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:13:08 -06:00
shakespeare.diy 16d9c3ef1a Update theme system: Mew→dark, enhance pink theme, add theme icons and logo filters
- Rename "mew" theme to "dark" (Mew=dark, Light=light remain)
- Make pink theme more vibrant with increased saturation and adjusted hues
- Convert theme selector to dropdown menu with icons (Moon, Sun, Zap, Heart)
- Add CSS filters to MewLogo for pink and light themes with smooth transitions
- Keep Black and Pink themes as requested

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-17 00:10:34 -06:00
shakespeare.diy c743c7d458 Remove ... icon, add theme toggle, fix logout error
- Removed MoreHorizontal icon from account switcher trigger
- Fixed logout minified React error by properly closing popover before navigation
- Added theme toggle to account popover with Mew, Light, Black, Pink options
- Added Pink (pastel pink) theme CSS variables
- Updated Theme type to support 'light', 'dark', 'black', 'pink', 'system'

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 23:39:29 -06:00
shakespeare.diy e0b8785d7b Show Join button when logged out, Ditto-style account popover, hide account area when logged out
- LeftSidebar: Compose button becomes "Join" when logged out, opens signup dialog
- LeftSidebar: Account area in lower-left hidden when logged out
- LeftSidebar: Clicking account area opens Ditto-style popover with profile card, account switching, add account, and logout
- FloatingComposeButton: Shows join icon (UserPlus) when logged out, opens signup dialog

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 23:33:32 -06:00
shakespeare.diy 25d91f7adb Only show reaction emojis on post detail page, not in the feed
Add showEmojis prop to ReactionButton (default false). The emoji
indicators next to the count now only render when showEmojis is
true, which is only passed from PostDetailPage.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 23:25:50 -06:00
shakespeare.diy ca759ab243 Fix double-counting reaction: reset optimistic delta on publish success
The optimistic +1 delta was stacking on top of the refetched stats
(which already included the new reaction), causing the count to
jump by 2. Now we reset the delta to 0 on success so only the
real server count is displayed.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 23:23:55 -06:00
shakespeare.diy beeff37dc6 Add emoji picker for reactions with optimistic count updates
- Create ReactionButton component with Popover-based emoji picker
- Clicking the reaction button opens the full emoji picker (EmojiPicker)
- Selecting an emoji publishes a NIP-25 kind 7 reaction event with proper e/p/k tags
- Optimistically increments the reaction count and shows the user's chosen emoji
- Reverts optimistic update on publish failure
- Invalidates event-stats and event-interactions queries on success
- Replace static heart/like buttons in NoteCard and PostDetailPage with ReactionButton

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 23:23:03 -06:00
shakespeare.diy 9964d02775 Render npub/nprofile as @username mentions in embedded note previews
Parse nostr:npub1… and nostr:nprofile1… references within embedded note
card text and render them as clickable @username mentions (resolving the
display name via useAuthor), matching the style used in NoteContent.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 23:18:38 -06:00
shakespeare.diy fa32b45ee8 Strip nested nevent/note references from embedded note previews
Embedded note cards now strip out nostr:nevent1... and nostr:note1...
references from the content preview text, preventing recursive embedding
and keeping the preview clean.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 23:17:35 -06:00
shakespeare.diy d48d4dc8b6 Render nevent/note references as inline embedded post cards
- Create EmbeddedNote component with avatar, author name, timestamp,
  truncated content preview, and optional image thumbnail
- Update NoteContent tokenizer to detect note1 and nevent1 references
  and render them as embedded cards instead of plain text links
- Collapse whitespace around embedded note cards like link previews
- Clicking an embedded card navigates to the full post detail page

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 23:16:17 -06:00
shakespeare.diy 722202eede Increase link preview and YouTube embed spacing to my-2.5
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 23:05:45 -06:00
shakespeare.diy 9b2d26b6a0 Fix link preview spacing, bot headers, and add YouTube embeds
Spacing:
- Reduce link preview margins from my-2 to mt-1.5 mb-0.5
- Collapse whitespace on text tokens adjacent to block-level tokens
  (link-preview, youtube-embed) so newlines from the original content
  don't stack with the card's own spacing
- Filter out empty text tokens after trimming

Bot headers:
- Send proper User-Agent header ("Mew/1.0 Link Preview Bot") when
  fetching OG data so dynamic sites (SPAs, Twitter, etc.) return
  server-rendered HTML with OG meta tags instead of a JS shell
- Accept application/xhtml+xml in addition to text/html

YouTube embeds:
- Detect YouTube URLs (youtube.com/watch, youtu.be, /embed/, /shorts/)
  and extract video ID
- New YouTubeEmbed component renders a privacy-enhanced iframe player
  (youtube-nocookie.com) with proper 16:9 aspect ratio
- YouTube links render as playable inline embeds instead of OG cards

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 23:03:09 -06:00