Commit Graph

102 Commits

Author SHA1 Message Date
sam ec9f57476d ++ 2026-04-18 11:44:46 +05:45
sam 6a60612ba6 poll compose 2026-04-18 11:44:31 +05:45
Alex Gleason cc702027b0 Add native haptic feedback to all key interactions
Install @capacitor/haptics and add a centralized haptics utility
(src/lib/haptics.ts) that uses the native taptic engine on iOS/Android
and falls back to navigator.vibrate() on web.

Haptics added to:
- Switch component (covers 36+ toggle switches app-wide)
- PullToRefresh threshold (covers 15+ pages)
- MobileBottomNav tab taps
- ReactionButton (like/unlike, double-click heart)
- RepostMenu (repost/undo repost)
- ZapDialog button press + payment success (NWC and WebLN)
- FollowButton and ProfilePage follow toggle
- ComposeBox (post, voice message, and poll publish success)
- NoteMoreMenu (bookmark, pin, mute)
- VinesFeedPage reaction and repost buttons
- ProfileReactionButton and ExternalReactionButton
- NoteCard share button
- BlobbiRoomShell swipe navigation

Replaces raw navigator.vibrate() calls in GameControls and
SendAnimation with the new cross-platform haptics utility, fixing
haptic feedback on iOS where the Vibration API is not available.
2026-04-14 11:06:18 -05:00
Chad Curtis 8f6d52a9f9 Fix embedded quote rendering: use NoteContent for DRY media/blobbi display
- Fix stateful global regex bug (IMETA_MEDIA_URL_REGEX) causing every
  other URL to be misclassified when used with .test() in loops; add
  non-global IMETA_MEDIA_URL_TEST_REGEX for safe .test() calls
- Rewrite EmbeddedNoteCard to render content via NoteContent (same as
  NoteCard) with a 260px height cap instead of reimplementing URL
  parsing and content truncation
- Pass disableEmbeds to NoteContent inside quotes to prevent recursive
  nostr:nevent/note references from spawning nested EmbeddedNote
  components
- Add overflow-aware 'Read more' toggle inline with attachment chips;
  fade gradient only renders when content actually overflows
- Add BlobbiStateCard rendering for kind 31124 in both EmbeddedNote
  and EmbeddedNaddr
- Extract EmbeddedCardShell with shared clickable card wrapper and
  author row, deduplicating ~150 lines across EmbeddedNoteCard,
  EmbeddedNaddrCard, and EmbeddedBlobbiCard
- Fix ComposeBox media URL detection using the same regex fix
- Fix EmbeddedNaddr profile links to use useProfileUrl instead of
  hardcoded npub paths
2026-04-13 18:03:09 -05:00
Chad Curtis 51a25919c7 Fix media rendering after quote posts in q-tag quotes
Video/audio/webxdc URLs were silently stripped from NoteContent's token
stream and rendered by parent components after NoteContent. When a quote
post's nostr: URI appeared at the end of the content, media was placed
after the quote embed instead of before it.

Render all media inline within NoteContent at their original content
position via a new media-embed token type. Remove the now-unused
NoteMedia component and the separate media rendering in NoteCard,
PostDetailPage, and ComposeBox.

Also:
- Append media-embed tokens for imeta-declared media not in content
  (gated to text note kinds 1/11/1111 only)
- Sanitize imeta-sourced URLs via sanitizeUrl()
- Skip useAuthor query when no media-embed tokens exist
- Memoize author display name derivation
2026-04-13 17:10:16 -05:00
Alex Gleason 6f53f7ad99 Fix avatar fallback showing '?' instead of name initial
ComposeBox and LeftSidebar avatar fallbacks only checked metadata.name,
ignoring display_name and genUserName. Now uses the same fallback chain
as ProfileCard: display_name -> name -> genUserName(pubkey). Also fixed
the getDisplayName helper in LeftSidebar to check display_name.
2026-04-11 15:36:47 -05:00
Dmitriy E 47831ffa64 fix: intermittent mobile compose modal collapse and unclickable input 2026-04-05 10:27:43 -05:00
Alex Gleason 29696fa3d3 Apply nearest-neighbor scaling to small custom emoji images
Custom emoji images with natural dimensions <= 16x16 now render with
image-rendering: pixelated to preserve crisp pixels instead of blurring.

Also consolidates 6 direct <img> sites to use the shared CustomEmojiImg
component so all custom emoji rendering benefits from this behavior.
2026-04-04 22:58:42 -05:00
Chad Curtis be82cb9626 Propagate relay and author hints to all event fetch call sites
Wire relay URL hints (from e/E tag position [2]) and author pubkey hints
(from e/E tag position [4] or p/P tag fallback) through every component
that fetches a referenced event:

- NoteCard: use getParentEventHints, pass hints through ReplyContext
- ReplyContext: accept and forward relay/author hints to EmbeddedNote
- CommentContext: extract hints from E/A tags in parseCommentRoot,
  pass to useEvent, useAddrEvent, and EmbeddedNote
- NotificationsPage: extract hints from e tag in ReferencedNoteCard
- usePollVoteLabel: extract hints from e tag for parent poll fetch
- ComposeBox: pass quotedEvent.pubkey as authorHint to EmbeddedNote
2026-04-04 06:03:33 -05:00
Chad Curtis 119f684fb3 Fix sharp corners on compose box by adding rounded-2xl 2026-04-03 22:41:16 -05:00
Chad Curtis 45134ef9cc Allow file uploads in poll composer
Remove the separate pollQuestion state and poll builder branch. Poll
mode now reuses the normal textarea/preview ternary (with edit/preview
toggle, file uploads, paste handling, imeta tags) and renders poll
options and settings below it.
2026-04-03 22:29:32 -05:00
Chad Curtis 1afa78ae39 Merge branch 'fix/disappearing-post-box' into 'main'
Fix disappearing compose box after posting

See merge request soapbox-pub/ditto!141
2026-04-02 06:29:56 +00:00
Chad Curtis 71f8ee0e16 fix: support accented and Unicode characters in hashtags
Replace /#\w+/g with /#[\p{L}\p{N}_]+/gu across all hashtag regexes
so that hashtags like #Bíblia and #verdade parse correctly. Affects
NoteContent, BioContent, ComposeBox, and PhotoComposeModal.
2026-03-31 06:48:36 -05:00
Chad Curtis 80820ae9c4 Expand compose textarea smoothly as you type 2026-03-30 07:36:16 -05:00
Lemon a80b306248 Reset feed composer to collapsed state after posting 2026-03-28 23:11:47 -07:00
Lemon c8c294a8ad Match ComposeBox background opacity with header and subheader (bg-background/85) 2026-03-28 23:11:47 -07:00
Alex Gleason 12d578ff57 Improve bundle chunking: lazy-load emoji picker, markdown, and remove runtime tailwind config
- Hardcode md breakpoint in useIsMobile and toaster to eliminate runtime
  import of tailwind.config (was pulling in tailwindcss, postcss-selector-parser,
  and plugin code ~100KB)
- Lazy-load EmojiPicker in ComposeBox (emoji-mart + data ~500KB deferred)
- Dynamic import webxdcMeta.ts (smol-toml + fflate only loaded for .xdc uploads)
- Lazy-load ArticleContent, PullRequestCard, CustomNipCard in NoteCard and
  PostDetailPage (react-markdown + unified pipeline ~147KB deferred)
- Consolidate 60+ lucide-react icon micro-chunks into a single chunk via
  manualChunks, reducing HTTP request overhead
- ReplyComposeModal chunk: 808KB -> 296KB (-63%)
- JS file count: 226 -> 181 (-45 files)
2026-03-27 23:44:20 -05:00
Mary Kate Fain 1f5ce2546c Add emoji picker and shortcode autocomplete to zap comment box
Extract shared useInsertText hook to DRY up the duplicated text
insertion logic across ComposeBox, DMChatArea, and ZapDialog.
Add EmojiPicker (GUI) and EmojiShortcodeAutocomplete (:shortcode
typing) to the zap comment textarea, and also add shortcode
autocomplete to the DM chat input which was previously missing it.

Closes #176
2026-03-27 17:46:16 -05:00
Chad Curtis 01c32a0e88 Restyle tabs and compose box: arc borders, opacity, layout order 2026-03-24 07:06:12 -05:00
Alex Gleason 6d71fcaa45 Move image quality setting to Network Settings page
Remove the inline toggle from the post composer toolbar and add
imageQuality to AppConfig instead. The setting now lives in
Settings > Network under 'Image Uploads' with a Compressed/Original
pill toggle, persisted to localStorage like other app settings.
2026-03-23 00:44:30 -05:00
Alex Gleason 235abf6add Add image quality toggle to post composer
Adds a Compressed/Original toggle button in the ComposeBox toolbar.
When set to Original, images are uploaded as-is without resizing or
re-encoding. Defaults to Compressed.
2026-03-23 00:35:31 -05:00
Alex Gleason e83f550b89 Resize and convert images to JPEG before upload in post composer
Images attached via ComposeBox are now resized (max 1920px) and
converted to JPEG before uploading, matching the BackgroundPicker
behavior. Non-image files (audio, video, .xdc) are unaffected.
Blurhash is computed from the resized file for consistency.
2026-03-21 10:46:43 -05:00
Chad Curtis 176813dce0 feat: support multi-photo upload in media picker
Add the  attribute to the file input in ComposeBox and update
the onChange handler to iterate over all selected files, uploading each
one via handleFileUpload, enabling users to select and attach multiple
files at once.
2026-03-18 05:55:41 -05:00
Dirk Rost 9309308a4e Merge branch 'fix/iOS-accessibility' into 'main'
fix: prevent iOS Safari auto-zoom on input focus

See merge request soapbox-pub/ditto!84
2026-03-17 19:55:18 +00:00
Lemon f91a37914c fix: prevent iOS Safari auto-zoom on input focus 2026-03-17 19:55:18 +00:00
Chad Curtis f1c15b64a2 Add poll creation to compose toolbar and polls page FAB 2026-03-17 19:55:18 +00:00
Alex Gleason 53c1fb1090 Fix quote posts for addressable events: use naddr instead of nevent
Addressable events (kinds 30000-39999) like lists get new event IDs when
updated, causing nevent references to break. Use naddr coordinates
(kind:pubkey:d-tag) which remain stable across updates.
2026-03-15 04:24:48 -05:00
Alex Gleason f92f50d5e0 Fix quote posts not loading: include relay hints in nevent URIs and q tags 2026-03-15 04:19:02 -05:00
Alex Gleason 10bb16abfb Clean up avatar shape code: remove magic strings, type casts, and inconsistencies
- avatar.tsx: consolidate to single isEmojiShape boolean, fix AvatarFallback using same logic as Avatar
- avatarShape.ts: accept NostrMetadata directly in getAvatarShape (no cast needed)
- Remove 'as Record<string, unknown>' casts from all ~50 call sites
- Replace 'circle' magic string with empty string in form defaults and parseShape
- Use isValidAvatarShape instead of string comparison in save logic
- ProfileCard: extract IIFE overlay style into useMemo, use isEmojiShape throughout
2026-03-13 21:14:03 -05:00
Alex Gleason 3b79b384e5 Add avatar shapes: users can set a shape (circle, triangle, hexagon, star, etc.) for their avatar via kind 0 metadata
- New 'shape' property on kind 0 profile metadata with 7 predefined shapes:
  circle, triangle, inverted-triangle, hexagon, star, inverted-star, hexagram
- Avatar component updated with clip-path support for non-circle shapes
- Visual shape picker added to both EditProfileForm and ProfileSettings
- Shape applied across all 49 avatar render sites in the app
- New UserAvatar wrapper component for future simplified avatar rendering
- Unknown shape values gracefully fall back to circle (forward compatible)
2026-03-13 16:14:40 -05:00
Lemon f0e04e2569 Convert emoji/GIF/stickers picker from popover to centered dialog
The picker was positioned relative to the trigger button via a Popover,
which felt awkward especially on smaller screens or inside other modals.
Now renders as a centered Dialog with a backdrop for a more traditional
modal experience.
2026-03-06 23:16:30 -08:00
Mary Kate Fain eba4121d63 Support hyphens in custom emoji shortcodes
All shortcode regexes used [a-zA-Z0-9_] which excluded hyphens,
causing custom emojis like :GM-Chachi: to fail at every stage:
publishing (no emoji tag added to event), rendering (shortcode not
resolved to image), and detection (not recognized as custom emoji).

Updated the character class to [a-zA-Z0-9_-] across all 8 locations
in NoteContent, CustomEmoji, ComposeBox, and customEmoji utility.

Also added viewer's custom emoji collection as a fallback in
NoteContent so shortcodes render even when events from other clients
omit the emoji tag.

Closes #62
2026-03-04 23:51:19 -06:00
Chad Curtis 463a621488 Fix emoji picker blur, tab alignment, and consistent picker sizing 2026-03-04 04:03:09 -06:00
Chad Curtis a914efacfd Add Stickers tab to emoji/GIF picker using custom emoji packs 2026-03-04 03:45:01 -06:00
Chad Curtis ededafa8de Merge emoji and GIF into single tabbed picker in ComposeBox 2026-03-04 03:37:52 -06:00
Alex Gleason b067b2da3e fix: render webxdc in compose preview by extracting shared NoteMedia component
The composer preview tab was missing webxdc embeds because it manually
rendered only videos and audio, while webxdc rendering lived exclusively
inside NoteCard. Extract NoteMedia into a shared component and
ImetaEntry/parseImetaMap into a shared utility, then wire them into the
ComposeBox preview with proper imeta tag generation on the mock event.
2026-03-03 11:28:19 -06:00
Chad Curtis b23a7b07f0 optimistically bump reply count after posting a comment 2026-03-03 00:48:12 -06:00
Chad Curtis 64c9936014 fix vine comments and toast visibility
- ComposeBox: invalidate the correct 'event-comments' cache key after
  posting a NIP-22 comment so CommentsSheet refreshes immediately
- toast: raise z-index to 300 so it renders above the CommentsSheet
  backdrop blur (which sits at z-200)
2026-03-03 00:45:31 -06:00
Alex Gleason 543eda02ee Fix linter errors: conditional hook, unused vars/imports, and unstable deps 2026-03-02 10:24:32 -06:00
Mary Kate Fain 5561240a47 Show preview toggle when compose content contains custom emojis 2026-03-01 22:15:23 -06:00
Mary Kate Fain 8df5fa7d16 Remove superseded files, fix quick reactions and compose preview for custom emojis
- Remove CustomEmojiPicker.tsx and useUserEmojiPacks.ts (replaced by
  Derek's useCustomEmojis + emoji-mart custom categories)
- Update EmojiPackContent to use local kind 10030 query
- Update EmojiShortcodeAutocomplete to use useCustomEmojis
- Fix quick reaction buttons to render custom emojis as images
- Fix quick reaction buttons to include emoji tag when reacting
- Filter stale custom emojis from quick reaction row
- Add emoji tags to compose box mock event for preview rendering
2026-03-01 22:04:44 -06:00
Derek Ross addbece173 Add NIP-30 custom emoji picker support (kind 10030)
Integrate custom emojis from the user's kind 10030 emoji list into
the emoji picker, compose flow, reactions, and DMs:

Hook:
- useCustomEmojis: query kind 10030, extract emoji shortcode/url pairs

EmojiPicker:
- Accept customEmojis prop, render as dedicated 'Custom' tab via
  emoji-mart's custom categories feature
- Changed onSelect from string to EmojiSelection union type
  (native unicode | custom shortcode+url)

ComposeBox:
- Pass custom emojis to picker, insert :shortcode: on selection
- Scan content on publish for :shortcode: patterns, cross-reference
  against user's emoji list, add ['emoji', name, url] tags (NIP-30)

QuickReactMenu:
- Custom emoji reactions: content=':shortcode:', with emoji tag
- Optimistic cache update includes custom emoji URL/name

DMChatArea:
- Pass custom emojis to picker, insert :shortcode: text

Settings:
- Registered as 'Custom Emojis' in Other Stuff > Whimsy section
- showCustomEmojis toggle (default true) gates all custom emoji
  features — when disabled, no kind 10030 queries are made
- Fix pre-existing Bot icon missing import in sidebarItems
2026-03-01 20:28:08 -05:00
Derek Ross 239d958e1f Add NIP-A0 voice message support (kind 1222/1244)
Implement recording, publishing, and rendering of NIP-A0 voice messages:

- VoiceMessagePlayer: compact waveform bar player when imeta waveform
  data is available, falls back to existing AudioVisualizer sinewave
- useVoiceRecorder: MediaRecorder hook with real-time amplitude capture,
  60s max duration, and waveform sampling for NIP-A0 imeta tags
- ComposeBox: mic button in toolbar with recording UI (live waveform,
  timer, cancel/send controls), publishes kind 1222 (root) or 1244
  (NIP-22 reply) with imeta waveform and duration fields
- NoteCard + PostDetailPage: render kind 1222/1244 via VoiceMessagePlayer
- Feed settings: voice messages registered in Other Stuff > Media with
  feed toggle (feedIncludeVoiceMessages), defaults to off
2026-03-01 16:36:14 -05:00
Chad Curtis e1e5bbe696 Render audio uploads as video-style visualizer with avatar and sinewave
- Add AudioVisualizer component: mirrors VideoPlayer chrome exactly (same overlay controls, progress bar, hide-on-idle, scroll-pause) with a canvas sinewave and author avatar in the centre
- Wire audio into NoteCard (kind 1 notes), FileMetadataContent (kind 1063), and ComposeBox preview
- Extract shared utilities: formatTime, usePlayerControls hook, and mediaUrls (regexes + mimeFromExt) to eliminate duplication across VideoPlayer, AudioVisualizer, NoteCard, NoteContent, ComposeBox, and EmbeddedNote
- Fix ComposeBox: audio URLs now get correct imeta MIME tags and are excluded from link-embed detection
2026-02-28 23:35:17 -06:00
Chad Curtis 38c7e00ca9 Share button on /i/ page opens compose modal pre-filled with URL 2026-02-28 21:27:09 -06:00
Alex Gleason d27464d6fc Add GIF keyboard with Tenor API to compose box and DM chat
Introduces a GIF picker powered by Tenor's API with search and trending
GIFs. The picker is integrated into both the post compose toolbar and
the DM chat input area, alongside a new emoji picker for DMs.
2026-02-28 21:13:59 -06:00
Alex Gleason 930e4dc006 Use FAB + full compose dialog for comments on external content pages
Widen ComposeBox.replyTo and ReplyComposeModal.event to accept URL,
enabling the standard post composer for NIP-22 comments on external
content. Replace the inline CommentForm on /i/ pages with a FAB that
opens ReplyComposeModal, giving users the full compose experience
with file uploads, emoji picker, and content warnings.
2026-02-27 23:45:27 -06:00
Alex Gleason 54a33bd108 Replace CommentsSection with NoteCard threading on external content pages
Use the same NoteCard-based threaded comment display on /i/ pages
that the rest of the app uses for post detail views. This replaces
the custom recursive Comment tree with the standard threaded layout
(direct replies + first sub-reply preview).

Also fix PostDetailPage and ComposeBox to handle I (external content)
tags when reconstructing the root for kind 1111 comments, enabling
proper threading when viewing or replying to comments on URLs.
2026-02-27 23:30:21 -06:00
Alex Gleason 515d817e1a Render kind 1111 comments as NoteCards on non-kind-1 post detail pages
Use useComments hook to fetch NIP-22 comments for non-kind-1 events
instead of useReplies (which only fetches kind 1), and render them
with the same NoteCard threaded layout used for kind 1 replies.
Also invalidate comments cache when posting NIP-22 replies.
2026-02-27 21:20:23 -06:00
Alex Gleason 6980d09018 Refactor NIP-22 replies to use usePostComment hook instead of inline tag construction
Removes duplicated NIP-22 tag logic from ComposeBox and delegates to
usePostComment, which correctly handles addressable/replaceable events
without adding spurious e tags. When replying to a kind 1111 comment,
the root event info is reconstructed from the comment's uppercase tags.

Added optional tags parameter to usePostComment so ComposeBox can pass
through hashtags, mentions, imeta, CW, and quote tags.
2026-02-27 18:26:44 -06:00