Commit Graph

170 Commits

Author SHA1 Message Date
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 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
Mary Kate Fain 575c771ca2 Rename sidebar label from 'Emoji Packs' to 'Emojis' 2026-03-04 23:30:57 -06:00
Mary Kate Fain 1352e1b617 Rename /emoji-packs to /emojis and remove Emojiverse link
- Update route path, sidebar item, extraKinds route, and NoteCard
  noun route from /emoji-packs to /emojis
- Remove Emojiverse external link from EmojiPackContent cards
- Remove sites array from emoji-packs kind definition

Closes #60
2026-03-04 23:28:53 -06:00
Mary Kate Fain 5e8c1e2e92 Refactor theme feed: remove Vibe sidebar item, consolidate into tabbed Themes page
Move the ThemeSelector (formerly on the Vibe/settings/theme page) into the
Themes page as the first 'My Themes' tab, alongside new 'Follows' and
'Global' feed tabs for browsing published themes.

- Delete ThemeSettingsPage and ThemeBuilderPage (builder lives inline in ThemeSelector)
- Remove Vibe sidebar entry and settings section
- Add useThemeFeed hook for follows/global theme event filtering
- Rename 'Vibe' to 'Theme' in account popover dropdown
- Update all links from /settings/theme to /themes
2026-03-04 20:28:05 -06:00
Mary Kate Fain 706816478c Add Clear Sky Vibes theme preset with magenta primary and cyan text 2026-03-04 19:40:23 -06:00
Mary Kate Fain 8f140c4eaa Add bundled fonts and background images for all 8 new theme presets
Install Cinzel, Nunito, Quicksand, Courier Prime, and Comic Neue as
bundled fonts and register them in the font registry. Add Unsplash
background images to every new preset (Gothic, Cottage, Candy Heart,
Midnight, Hologram, Floret, Win 95, Plush).
2026-03-04 19:34:24 -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 71e7d6e211 Fix reaction shown on wrong post: use NIP-25 last e-tag for kind 7 in getParentEventId 2026-03-03 16:24:31 -06:00
Alex Gleason f6a3d6c618 Fix kind 7 reaction e-tag extraction to use last e-tag per NIP-25 2026-03-03 16:03:37 -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
Mary Kate Fain be276d5083 fix: show back arrow to /books on all viewports and rename header to 'Books'
Remove sidebar:hidden from the back arrow on ISBN pages so it is
always visible, and change the header label from 'Book' to 'Books'.
2026-03-03 10:25:57 -06:00
Mary Kate Fain f41c6a4126 feat: enhance Books feed with tabs, truncation, reviews, and comments
- Add Following/Global feed tabs to Books page with sticky tab bar
- Add KindInfoButton linking to Bookstr (bookstr.xyz) for content creation
- Add text truncation with 'Read more' fade (300px max-height, matching NoteCard)
- Include kind 1111 book comments in the feed with 'commented' badge
- Comments on books navigate to the book page instead of event detail
- Add Reviews tab on book detail pages (/i/isbn:*) alongside Comments
- Add review submission form with 5-star rating, spoiler warnings, and edit support
- Add 'Write Review' button in book page action bar and Reviews tab
- Support useFollowList for follows-tab scoped book queries
2026-03-03 10:25:57 -06:00
Mary Kate Fain 4691412444 feat: replace Books page with Nostr book feed using Bookstr NIP protocol
Replace the static OpenLibrary trending books list with a live social
feed of book-related Nostr events. The feed queries kind 31985 (book
reviews/ratings) and kind 1 posts tagged #bookstr or referencing ISBNs,
following the Bookstr NIP-XX protocol.

New files:
- src/lib/bookstr.ts: protocol constants, event parsing, ISBN extraction
- src/hooks/useBookFeed.ts: infinite scroll feed of book events
- src/hooks/useBookDetails.ts: OpenLibrary book metadata by ISBN
- src/components/BookFeedItem.tsx: feed item with inline book card,
  star ratings, spoiler guards, and full social actions
2026-03-03 10:25:57 -06:00
Chad Curtis 96fe271a65 fix: remove events from onboarding content picker, use Calendar icon 2026-03-03 06:39:43 -06:00
Chad Curtis dee47c0907 fix: prevent bare dot from triggering NIP-05 redirect in search 2026-03-03 04:33:10 -06:00
Chad Curtis c3010a6282 Add sidebar divider support, only available in edit mode 2026-03-02 03:20:26 -06:00
Chad Curtis ce8f9bb8fc Sync page header icons with sidebar (BookMarked for Books, Earth for World) 2026-03-02 03:12:44 -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
Alex Gleason ca97d674b6 Move Books higher in sidebar, right after Articles, with distinct icon 2026-03-01 23:14:36 -06:00
Alex Gleason 468b66f9d4 Merge branch 'main' of gitlab.com:soapbox-pub/ditto 2026-03-01 23:13:38 -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
Alex Gleason 662d290305 Add Books page with trending books from OpenLibrary and show book titles in comment context
- Add /books page displaying popular books fetched from OpenLibrary's trending API
- Add Books entry to sidebar registry so users can add it from the More menu
- Add /books route to AppRouter
- Show book title instead of raw 'isbn:...' in comment context by fetching book info via useBookInfo hook
2026-03-01 23:02:49 -06: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 fef31b8079 Remove duplicate Custom Emojis entry from Whimsy section in content settings 2026-03-01 22:57:23 -06:00
Alex Gleason 9717312d34 Show country at bottom unless exact match or no profile results
Country suggestion only appears at the top of search results when
the query is an exact match on name or code. For prefix matches
(e.g. 'angol' -> Angola), the country appears below profile results.
If there are no profile results, the country is shown at the top.
2026-03-01 22:43:34 -06:00
Alex Gleason 3b6325af7e Match countries by name prefix instead of exact match only
Queries like 'angol' now match 'Angola'. Prefers exact code matches
first, then the shortest prefix match. Requires at least 2 characters.
2026-03-01 22:40:04 -06:00
Alex Gleason a1de680f72 Restrict country search suggestion to exact name/code match only
Show at most 1 country result, and only when the query exactly
matches a country name or ISO code (case-insensitive).
2026-03-01 22:38:24 -06:00
Alex Gleason 8280f0ee2d Add country suggestions to sidebar search autocomplete
Typing a country name in the search bar now shows matching countries
with their flag emoji, navigating to /i/iso3166:<CODE> on click.
Works on both desktop (ProfileSearchDropdown) and mobile
(MobileSearchSheet) with full keyboard navigation support.
2026-03-01 22:31:24 -06:00
Alex Gleason e5e7dd6564 Merge branch 'main' of gitlab.com:soapbox-pub/ditto
# Conflicts:
#	src/lib/sidebarItems.tsx
2026-03-01 22:23:17 -06:00
Alex Gleason 9d52e3a42c Add World page with country flags grid at /world
Browse all countries in a searchable grid. Clicking a flag navigates
to the existing /i/iso3166:<CODE> external content page. The World
item is also registered in the sidebar so users can add it via the
More menu.
2026-03-01 22:20:38 -06:00
Mary Kate 95deb01787 Merge branch 'emoji-packs' into 'main'
Add NIP-30 custom emoji packs support

See merge request soapbox-pub/ditto!24
2026-03-02 04:19:54 +00:00
Alex Gleason fad790be16 Extract non-component exports to separate modules to fix react-refresh warnings
Move helper functions, types, and constants out of component files into
dedicated lib/hooks modules so each component file only exports components:

- getContentWarning -> src/lib/contentWarning.ts
- isCustomEmoji, getCustomEmojiUrl, buildEmojiMap, ResolvedEmoji, resolveReactionEmoji -> src/lib/customEmoji.ts
- ExternalContent, parseExternalUri, formatIsbn, headerLabel, seoTitle -> src/lib/externalContent.ts
- extractYouTubeId, extractTweetId, and other URL helpers -> src/lib/linkEmbed.ts
- isSingleImagePost -> src/lib/noteContent.ts
- useOnboarding, OnboardingContext -> src/hooks/useOnboarding.ts

Also un-exported module-private symbols in VinesFeedPage and SettingsPage.
2026-03-01 22:15:47 -06: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 13dcad1070 Update events blurb to mention creating events on Plektos 2026-03-01 19:29:12 -05:00
Derek Ross 1e40024100 Include kind 31922 in general feed when events toggle is on
The events entry in extraKinds only registered kind 31923 (time-based),
so date-based calendar events (kind 31922) were missing from the
general feed. Added extraFeedKinds field to ExtraKindDef and used it
to include 31922 alongside 31923 when feedIncludeEvents is enabled.
2026-03-01 19:20:15 -05:00
Alex Gleason 817a0077cf Add AI Chat as a sidebar item
Registers 'ai-chat' in SIDEBAR_ITEMS with the Bot icon, linking to
/ai-chat. Requires authentication since the Shakespeare API needs a
logged-in Nostr account.
2026-03-01 18:18:08 -06: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
Mary Kate Fain b63bfefd3a Replace Scroll icon with Settings cog icon for settings 2026-03-01 12:51:55 -06:00