Commit Graph

89 Commits

Author SHA1 Message Date
Lemon 31e6810fcb Disable decorative arcs on vines page for clean full-bleed video 2026-03-22 23:36:40 -07:00
Lemon a8f2c54dac Add decorative arc to logo header when no sub-header exists
Introduces a hasSubHeader layout option. Pages with their own
tab bars set hasSubHeader: true, suppressing the arc on the
mobile top bar. Pages without tab bars (Bookmarks, Trends, etc.)
get the arc on the logo header automatically, ensuring the
bottom-most header always has the curved element.
2026-03-22 23:33:06 -07: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
Dirk Rost 0c519a9cd9 Merge branch 'fix/account-switch-stale-data' into 'main'
Fix stale user data persisting across account switches

Closes #94

See merge request soapbox-pub/ditto!95
2026-03-22 01:48:13 +00:00
Mary Kate 8dd9b72630 Fix stale user data persisting across account switches 2026-03-22 01:48:13 +00:00
Alex Gleason 4b1f015d97 Include kind 62 vanish events in the home feed
Register kind 62 in the EXTRA_KINDS feed system so Request to Vanish
events from followed users appear in the home feed. Enabled by default
via the feedIncludeVanish toggle, and can be disabled in feed settings.
2026-03-20 23:35:50 -05:00
Alex Gleason 44e5c1719f Expand Messages page to full width by hiding right sidebar 2026-03-16 23:24:42 -05:00
Alex Gleason d7461e0488 Add bottom overscroll padding to center column for visual comfort and bottom bar clearance 2026-03-15 23:17:15 -05:00
Chad Curtis f200359059 Implement BUD-03 kind 10063 Blossom server list management
Blossom servers now work exactly like relays:
- App default servers (APP_BLOSSOM_SERVERS) can be toggled on/off
- User's kind 10063 server list is fetched and synced from Nostr
- NostrSync fetches kind 10063 on login and applies it to config
- useInitialSync fetches kind 10063 in parallel on first login
- BlossomSettings UI mirrors RelayListManager with two sections:
  App Blossom Servers (with enable toggle) + Your Blossom Servers
- User changes publish a kind 10063 replaceable event to Nostr
- getEffectiveBlossomServers() merges app + user servers like getEffectiveRelays()
- Migration from old blossomServers[] to blossomServerMetadata handled in AppProvider
2026-03-14 12:34:59 -05:00
Alex Gleason f74d333d63 Fix mobile bottom nav not hiding on swipe in Vines page
The Vines page uses an internal snap-scroll container instead of window
scroll, so useScrollDirection (which listened to window.scrollY) never
detected any scroll movement. Add a scrollContainer option to
LayoutOptions so pages with custom scroll containers can communicate
the element to MobileBottomNav, which passes it to useScrollDirection.
2026-03-14 01:08:45 -05:00
Dirk Rost b9c590598d Merge main into feature/development-feed and fix lint issues
Resolve merge conflicts from badges, books, help, and emojis rename.
Fix biome lint: add type="button" to all buttons, use template literals,
format new components with project conventions.
2026-03-13 16:34:36 -05:00
Alex Gleason b3b2e4f077 Move Plausible config into AppConfig (supports .env and ditto.json) 2026-03-11 14:35:47 -05:00
Derek Ross 04fa292fbb Add Nostr Badges (NIP-58) as a new content type
Implement badge definitions (kind 30009) and profile badges (kind 30008)
as a new Whimsy content type with feed page, NoteCard rendering, sidebar
entry, dedicated detail page with awardee list, and a Badges core profile
tab showing a user's accepted badges with links to badge definitions.
2026-03-08 17:46:13 -04:00
Alex Gleason 2e6d328335 Remove mobile bottom navigation bar
Delete MobileBottomNav component and all related code including the
noBottomSpacer layout option, bottom nav height spacer, and CSS
calculations that accounted for the bottom nav height.
2026-03-07 11:57:17 -06:00
Lemon 54c4d7e47c Fix GIF picker scroll inside dialogs by portaling into modal container
Radix Dialog's RemoveScroll blocks wheel events on elements outside the
Dialog's DOM tree. The GIF picker Popover was portaling to document.body,
placing it outside the RemoveScroll boundary. Trackpad scrolling was
blocked while scrollbar dragging still worked.

Added PortalContainerContext so PopoverContent can portal into the modal
element instead of document.body when rendered inside a dialog/modal.
2026-03-06 23:16:30 -08:00
Chad Curtis c7f3211b7d Add user lists, follow packs, home feed tabs, and list-based feed filtering
- Add NIP-51 Follow Sets management (useUserLists, UserListsPage at /lists)
- Add Follow Packs support (useFollowPacks) for curated people lists
- Add FeedEditModal for creating/editing home feed tabs with author scope,
  kind picker, and list/pack author population
- Add AddToListDialog accessible from the note more-menu
- Unify home feed tab resolution with profile tabs via useResolveTabFilter,
  supporting $follows variable expansion
- Wire People filter in Search and FeedEditModal with list/pack picker
- Extract shared ScopeToggle, ListPackPicker, parseSelectedKinds primitives
  into SavedFeedFiltersEditor to eliminate duplication across three call sites
- Cache savedFeeds in AppConfig for instant tab render on load
- Strip orphaned profile-destination entries from encrypted settings on read
2026-03-06 17:23:47 -06:00
Alex Gleason 9ea8788b92 Replace kind 16769 SavedFeedFilters with standard NIP-01 filters and variable tags
The proprietary SavedFeedFilters JSON schema in kind 16769 tab events was
non-interoperable. Replace it with standard NIP-01 filter objects that any
Nostr client can execute, plus a variable system (var tags) for dynamic
values like follow lists.

- Tab filters are now standard NIP-01 filters with optional NIP-50 search
- Variables ($name) in filters are resolved via var tags that extract tag
  values from referenced events (e.g. follow list pubkeys from kind 3)
- $me is the only runtime variable (profile owner's pubkey)
- Variables in arrays are expanded in-place (spliced)
- Add useResolveTabFilter hook for variable resolution with caching
- Update all consumers: ProfilePage, Feed, SearchPage, ContentSettings
- Remove SavedFeedFilters type entirely in favor of TabFilter
2026-03-06 01:28:09 -06:00
Chad Curtis ba62b42ce0 Search filters: multi-person authors, sort toggle, icon polish
- Author scope 'person' renamed to 'people' supporting multiple authors;
  stored as repeated ?author= URL params; chips show avatar/name with
  individual remove buttons resolved via useAuthor
- Add sort filter: Recent / Hot / Trending — maps to NIP-50 sort:hot /
  sort:trending search terms, persisted to URL and SavedFeedFilters
- Both segment toggles (From / Sort) now show size-3.5 Lucide icons:
  Globe / Users / UserSearch for author scope; Clock / Flame / TrendingUp
  for sort
- SavedFeedFilters: authorPubkey→authorPubkeys[], add sort field; schema
  and all consumers (Feed.tsx, ProfilePage.tsx) updated accordingly
- useStreamPosts: resolves array of npub/hex pubkeys, applies sort: NIP-50
  term to initial search query
2026-03-05 21:14:30 -06:00
Chad Curtis 94878da5bc Add saved search feeds: home feed tabs and profile tabs
- SavedFeed type with destination field ('feed' | 'profile'), stored in
  EncryptedSettings and synced via NIP-78 across devices
- useSavedFeeds hook: add/remove/rename, accepts destination param
- Search page 'Add to…' popover with two tile options:
  - 'Home feed' — saves as a tab on the home page feed (always available)
  - 'Profile tab' — only shown for generic searches (no author filter set);
    auto-locks authorPubkey to the current user's pubkey so the tab shows
    your own posts matching that search; user-specific searches are
    ineligible since they already have an author scope
- Home feed (Feed.tsx) renders saved feeds as extra scrollable tabs after
  Follows/Community/Global, powered by useStreamPosts (live streaming)
- Profile page (ProfilePage.tsx) renders saved feeds where destination=profile
  and authorPubkey matches the viewed profile as custom tabs after Wall;
  tabs are scrollable, use shrink-0 layout to avoid stretching
- Both tab bars are horizontally scrollable to accommodate many tabs
- ProfileSavedFeedContent and SavedFeedContent share the same stream-based
  rendering: skeleton loading, empty state, live NoteCard list
2026-03-05 21:14:30 -06:00
Alex Gleason 35bc1a0438 Add configurable homepage route and show all items in mobile sidebar
- Add homePage config field (default: 'feed') that determines which
  sidebar item renders at the '/' route
- Any sidebar item can be set as the homepage via Feed settings
- The feed is always accessible at /feed when not configured as home
- Normalize sidebar item IDs: every item follows path === '/' + id
  (feed path changed from '/' to '/feed', emoji-packs renamed to emojis)
- Remove BOTTOM_NAV_ITEMS filter from MobileDrawer so all sidebar
  items are visible on mobile (feed, notifications, search included)
- Mobile bottom nav dynamically reflects the configured homepage icon
- Homepage setting syncs across devices via encrypted settings
- Add emoji-packs -> emojis migration for existing sidebar orders
2026-03-05 15:23:43 -06:00
Mary Kate Fain 438983cb13 Use Pencil icon for theme FAB instead of Plus
Add fabIcon prop to LayoutOptions and FloatingComposeButton so pages can
override the default Plus icon. The Themes page now shows a Pencil icon
on its FAB to indicate editing/customizing rather than composing.
2026-03-04 20:59:00 -06:00
Alex Gleason f1191299e8 Add Sentry integration with lazy loading and user-configurable DSN
Sentry SDK is loaded via dynamic import() only when a DSN is configured
and error reporting is enabled, keeping it out of the initial bundle.
Users can control error reporting via a toggle and DSN field in
Advanced Settings, with the DSN synced across devices via encrypted
NIP-78 settings. The ErrorBoundary now reports fatal crashes to Sentry
with component stack context, and a beforeSend hook censors nsec
private keys before any data leaves the browser.
2026-03-03 17:25:14 -06:00
Alex Gleason 11070c3d55 Revert FAB scroll-hide behavior — always show FAB
Reverts 6abe9fa and the fabHidden parts of 7f26beb. The FAB is now
always visible when showFAB is true, restoring sticky positioning
inside the center column. Removes IntersectionObserver logic from
Feed.tsx, fabHidden state from LayoutStore, and the useSetFabHidden
hook.
2026-03-03 15:22:57 -06:00
Derek Ross 1cbb7b7428 Add Development feed with NIP-34 git repos, patches, PRs, custom NIPs, and app submissions
New 'Development' section under Other Stuff in Content Settings that
aggregates developer-focused Nostr content: git repository announcements
(kind 30617), patches (kind 1617), pull requests (kind 1618), custom
NIP proposals (kind 30817), and app submissions (kind 31733).

Adds /dev route using KindFeedPage with external links to Gitworkshop
and NostrHub. Five new card components render each kind in the feed.
2026-03-03 13:23:34 -05:00
Chad Curtis aa04754c77 feat: wire Media Session API to video/livestream players and fix OS scrubber; hide mini-bar on mobile 2026-03-03 05:57:57 -06:00
Chad Curtis 4961cac19d fix: profile right sidebar persisting after navigation
The cleanup guard in useLayoutOptions compared store.getSnapshot() (a LayoutSnapshot spread copy) against prev.current (the original LayoutOptions reference), which are always different objects. The guard was never true, so store.reset() never fired on unmount and the ProfileRightSidebar leaked to other pages. Added getOptions() to expose the raw options reference for a correct identity comparison.
2026-03-03 01:21:02 -06:00
Derek Ross 6abe9fade0 Hide FAB while ComposeBox is visible, fade in on scroll
Use IntersectionObserver on the ComposeBox wrapper in Feed.tsx to track
its visibility. When visible, hide the FAB to avoid redundancy. When the
user scrolls past the compose area, the FAB smoothly fades in with an
upward slide animation. Extends LayoutStore with fabHidden state that
is part of the snapshot so useSyncExternalStore triggers re-renders.
2026-03-02 21:35:36 -05:00
Chad Curtis 225fa168a6 Implement Media Session API for Android notification panel controls 2026-03-02 00:36:02 -06:00
Mary Kate Fain 1098057905 Merge main into feat/nip-38-user-statuses
Resolve merge conflicts in 5 files, keeping both NIP-38 user statuses
and music/podcasts/events features from main:
- src/App.tsx
- src/contexts/AppContext.ts
- src/lib/extraKinds.ts
- src/lib/schemas.ts
- src/test/TestApp.tsx
2026-03-01 23:33:51 -06:00
Mary Kate Fain d46ce9d012 Merge origin/main into feature/music-podcasts
Resolve conflicts in AppRouter, NoteCard, sidebarItems, and PostDetailPage
by keeping both music/podcast features and main's additions (books,
calendar events).
2026-03-01 23:26:54 -06:00
Derek Ross bac91af1f8 Add user statuses to Settings > Content as a display toggle
Register kind 30315 in extraKinds as a feedOnly entry with showKey
(not feedKey) so statuses appear as a toggle in Other Stuff > Social
but are NOT included in the main feed query. The toggle controls
whether statuses are displayed on NoteCard author rows and profile
pages.

- extraKinds: registered with showKey='showUserStatuses', feedOnly=true
- ContentSettings: handle feedOnly entries with showKey as display toggles
- NoteCard: gate useUserStatus behind feedSettings.showUserStatuses
- ProfilePage: gate status display behind feedSettings.showUserStatuses
- Defaults: showUserStatuses=true (enabled by default)
2026-03-02 00:06:05 -05:00
Mary Kate Fain ba4ddff11b Merge remote-tracking branch 'origin/main' into feat/nip-52-calendar-events
# Conflicts:
#	src/AppRouter.tsx
#	src/components/InitialSyncGate.tsx
#	src/components/NoteCard.tsx
#	src/lib/sidebarItems.tsx
2026-03-01 23:01:39 -06:00
Derek Ross 6f31a681d7 feat: add podcast and music 2026-03-01 23:59:57 -05:00
Mary Kate Fain 068b3a667b Merge remote-tracking branch 'origin/main' into emoji-packs
# Conflicts:
#	src/components/NoteCard.tsx
#	src/contexts/AppContext.ts
#	src/lib/sidebarItems.tsx
#	src/test/TestApp.tsx
2026-03-01 22:13:51 -06:00
Mary Kate Fain 84d69b4426 Merge remote-tracking branch 'origin/feat/nip-30-custom-emoji-picker' into emoji-packs
# Conflicts:
#	src/App.tsx
#	src/components/ComposeBox.tsx
#	src/components/EmojiPicker.tsx
#	src/components/InitialSyncGate.tsx
#	src/components/QuickReactMenu.tsx
#	src/contexts/AppContext.ts
#	src/lib/schemas.ts
#	src/lib/sidebarItems.tsx
#	src/test/TestApp.tsx
2026-03-01 22:02:03 -06:00
Chad Curtis 00f612e2f2 merge streams into videos page, remove streams sidebar entry 2026-03-01 20:40:37 -06:00
Chad Curtis ca8ceed831 add NIP-68 photos and NIP-71 videos with instagram/youtube style pages 2026-03-01 20:40:23 -06:00
Mary Kate Fain 2a006145ca Add NIP-30 custom emoji packs support
- Sidebar: Add 'Emoji Packs' item linking to /emoji-packs feed (kind 30030)
- Feed: EmojiPackContent renders pack preview with emoji grid and add/remove button
- NoteCard: Kind 30030 events shown with SmilePlus header and pack content
- useUserEmojiPacks hook: Fetches user's kind 10030 list and resolves referenced
  kind 30030 packs into a flat emoji collection
- EmojiPicker: New 'Custom' tab shows user's NIP-30 emojis alongside native picker
- CustomEmojiPicker: Searchable grid of user's custom emojis grouped by pack
- ComposeBox: Custom emojis insert ':shortcode:' and add emoji tags to published events
- EmojiShortcodeAutocomplete: Suggests custom emojis alongside native ones as you type
- QuickReactMenu: Custom emoji reactions include NIP-30 emoji tags in kind 7 events
- Config: showEmojiPacks and feedIncludeEmojiPacks settings for sidebar/feed control
2026-03-01 20:36:53 -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 76bf856737 Add NIP-52 calendar event support (kind 31922/31923) with RSVPs
Implement full calendar event rendering, dedicated events page, and
RSVP functionality for NIP-52 calendar events:

Registration:
- FeedSettings: showEvents + feedIncludeEvents toggles
- extraKinds: kind 31923 registered as 'events' in social section
  with link to plektos.app for event creation/management
- Sidebar: Events entry with CalendarDays icon at /events
- Onboarding: added to content picker, pre-checked by default

Components:
- CalendarEventContent: rich card with cover image, title, date/time,
  location, description, participant count, hashtags. Handles both
  kind 31922 (date-based) and 31923 (time-based) with Intl formatting
- CalendarEventDetailPage: full detail view with RSVP UI (Going/Maybe/
  Can't Go buttons + optional note), attendee sections with avatar
  stacks, participant roles, share button, zap support
- RSVPAvatars: reusable overlapping avatar stack with tooltips

Hooks:
- useEventRSVPs: query kind 31925 RSVPs for an event, deduplicate by
  author, group by status (accepted/declined/tentative)
- useMyRSVP: check current user's RSVP status for an event
- usePublishRSVP: publish kind 31925 RSVP with cache invalidation

Pages:
- EventsFeedPage: two-tab layout — Upcoming (calendar events from
  follows/global with future-first sorting) and Activity (social feed
  showing 'Alice is going to [Event]' from followed users' RSVPs)

Wiring:
- NoteCard: isCalendarEvent detection + CalendarEventContent dispatch
- PostDetailPage: routes kind 31922/31923 to CalendarEventDetailPage
- AppRouter: /events route
2026-03-01 18:39:10 -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
Alex Gleason d0b49e78b6 Add appId/client config, use appName for client tags and zap comments
- Remove defaultZapComment config; derive from appName instead
- Add appId (string) for NIP-78 d-tag prefixes (e.g. ${appId}/metadata)
- Add client (optional NIP-89 addr) as third element of client tags
- Update useNostrPublish and useEncryptedSettings to use config.appName
  and config.client for client tags instead of location.hostname
- Replace hardcoded 'ditto-metadata' d-tag with ${config.appId}/metadata
- Remove Default Zap Comment setting from AdvancedSettings UI
2026-03-01 13:44:31 -06:00
Chad Curtis 708dd23fc4 Add follows feed reply filter and fix feedSettings deep-merge for new keys 2026-02-28 20:27:03 -06:00
Alex Gleason 713bf52cfb Add configurable appName to AppConfig, replacing hardcoded 'Ditto' in page titles and UI text
All runtime references to 'Ditto' now use config.appName, which defaults
to 'Ditto' and can be overridden at build-time via ditto.json. This
covers useSeoMeta titles across all pages, the welcome screen, feed CTA,
Nostr Connect app name, encrypted settings event title, and the
AdvancedSettings zap comment placeholder.
2026-02-28 17:09:37 -06:00
Chad Curtis e4cc47ba51 Add Magic settings page with Magic Mouse cursor fire effect
- Add /settings/magic entry to settings page with magic-intro.png icon
- New MagicSettingsPage with toggle for Magic Mouse mode
- Add magicMouse boolean to AppConfig (optional, defaults to false)
- CursorFireEffect: canvas overlay emitting fire particles in primary color
- Mount CursorFireEffect in MainLayout when magicMouse is enabled
2026-02-28 03:47:07 -06:00
Alex Gleason 6248b584e3 Rename 'active themes' to 'profile themes' in feed settings and labels 2026-02-27 16:07:58 -06:00
Alex Gleason e423cc8ec1 Render theme events (kind 16767/36767) as feed posts with full interactions
Migrate themes from a standalone card to the standard NoteCard rendering
pattern (like treasures), so theme events appear as interactive posts
with reply, repost, reaction, and zap buttons.

- Create ThemeContent component for inline theme color preview
- Add ThemeHeader for 'shared a theme' / 'updated their theme' labels
- Support both kind 36767 (definitions) and 16767 (active themes) in feeds
- Convert extraKinds themes entry to use subKinds pattern with toggles
- Create ThemesPage wrapper (like TreasuresPage) for sub-kind filtering
- Update ContentSettings to sync both sub-kind feed toggles
2026-02-27 15:58:05 -06:00
Alex Gleason 658c7d1dc1 Refactor Webxdc page to use standard Feed with tag filters
Replace the custom WebxdcFeedPage with KindFeedPage, powered by
a new tagFilters option on useFeed/Feed/KindFeedPage. The webxdc
page now uses the same follows/global tabs, pull-to-refresh, and
NoteCard rendering as every other feed.

- useFeed: accept optional tagFilters (e.g. { '#m': [...] })
- Feed/KindFeedPage: pass tagFilters through
- FloatingComposeButton: accept onFabClick callback override
- LayoutOptions: add onFabClick for custom FAB behavior
- Remove useWebxdcFeed (no longer needed)
2026-02-27 11:21:58 -06:00
Alex Gleason 8fae0cf97a Add /webxdc feed page and sidebar menu item
Query NIP-94 kind 1063 events filtered by #m tag for
application/x-webxdc MIME type. Includes infinite scroll,
pull-to-refresh, embedded webxdc launcher, and onboarding
content selection.
2026-02-26 22:21:33 -06:00
Alex Gleason 14f75ed50d Add auto-share theme toggle: automatically publish custom theme changes to profile 2026-02-26 19:53:46 -06:00