Commit Graph

164 Commits

Author SHA1 Message Date
Alex Gleason 11c4675e05 fix: use NIP-85 addr stats (kind 30384) for addressable events
useEventStats now accepts an optional event object. For addressable
events (kinds 30000-39999), it queries kind 30384 stats by the a-tag
coordinate instead of kind 30383 by event ID, so reply/reaction counts
display correctly for badge collections, badge definitions, articles,
and other addressable event types.

All 8 call sites updated to pass the event object.
2026-03-22 23:32:25 -05:00
Alex Gleason 85b47a7a62 Consolidate 4 duplicate vanish compact cards into VanishCardCompact
The same stripes + glitch icon + title + npub + reason layout was
duplicated across EmbeddedVanishCard, EmbeddedVanishPost,
VanishEventContent compact mode, and an inline version in NoteCard's
threaded branch. Extract a single VanishCardCompact component and
replace all four copies with it.

Net reduction of 76 lines.
2026-03-21 22:39:13 -05:00
Alex Gleason 58b28f7c1a Fix vanish card stripes overflowing in threaded ancestor view
The vanish layout (kind 62) in NoteCard didn't handle the threaded/
threadedLast props, so when rendered as a parent in AncestorThread,
it used the standard feed card layout where the caution stripes are
siblings of the bordered container — visually appearing to escape
above and below the card.

Add a dedicated threaded branch for vanish cards that:
- Uses the avatar column + connector line pattern like other threaded cards
- Places the stripes INSIDE the bordered container (as top/bottom bands)
  so they stay visually contained within the card boundary
2026-03-21 22:33:37 -05:00
Alex Gleason d980a8af5b Fix performance regression: cache parsed metadata and memoize NoteCard
The IndexedDB profile cache (d9e943d0) introduced a regression where
parseAuthorEvent() — which runs the full Zod n.metadata().parse() chain —
was called synchronously on every render via TanStack Query's initialData
option. With 50+ NoteCards in a feed and no React.memo, this caused ~970ms
of Zod validation + ~1.3s of Nostrify core overhead per tab navigation.

Fix by:
1. Storing pre-parsed metadata in ProfileCacheEntry so useAuthor can
   provide initialData without re-running Zod on every render
2. Wrapping NoteCard in React.memo to prevent re-renders when props
   haven't changed (event objects are referentially stable from the
   query cache)
2026-03-21 21:58:02 -05:00
Alex Gleason d0ef3e2575 Add dramatic NIP-62 Request to Vanish display (kind 62)
Implement a visually striking display for kind 62 vanish events with
animated caution stripes, glitch effects, and a grand exit aesthetic.
Since the user has nuked their account, the npub is shown instead of
profile info. Supports both compact feed cards and full detail view
with comments and sharing.
2026-03-20 23:17:25 -05:00
Alex Gleason 3e859f9b74 Redesign development kind cards with bordered card style and activity indicators
Wrap GitRepoCard, PatchCard, PullRequestCard, and CustomNipCard content
in rounded-2xl bordered containers matching the quote post / embedded
note visual pattern. Add KIND_HEADER_MAP entries for all four dev kinds
(30617, 1617, 1618, 30817) so the feed shows activity labels like
'shared a repository', 'submitted a patch', 'opened a pull request',
and 'proposed a NIP' above each post.
2026-03-20 23:01:48 -05:00
Alex Gleason a9449bd009 Fix jumpy home feed: layout shifts, background refetches, and pre-filtering
- Add min-height placeholder to InlineImage to prevent layout shifts when
  images load from 0px to natural height
- Remove 60s refetchInterval from useFeed that caused all pages to re-sort
  in the background, triggering full feed re-renders
- Pre-filter deprecated/hidden events at Feed level (shouldHideFeedEvent)
  instead of returning null inside NoteCard after hooks have already run
- Stabilize author info skeleton/loaded height with consistent min-h-[42px]
  to prevent cascading layout recalculations
2026-03-20 13:56:26 -05:00
Alex Gleason bbff34bd76 Add special rendering for Zapstore app events (kind 32267)
Render kind 32267 software application events with app icon, name,
summary, platform badges, screenshots, and action buttons in both
feed (NoteCard) and detail (PostDetailPage) views. Add useAddrEvent
fallback to wss://relay.zapstore.dev for kinds 32267/30063 so app
events are discoverable even when not on the user's relay set.
2026-03-19 17:08:40 -05:00
Derek Ross adc370a598 Fix live streams showing as active when ended, and fix muted icon state
Implement NIP-53 staleness heuristic (24h threshold) to mark abandoned
status=live streams as ended. Fetch all streams globally and filter
follows client-side (including p-tag participants for streaming services).
Add Live/Planned/Past pill tabs to the streams strip on /videos.

Also fix pre-existing bug where the volume icon showed unmuted while the
video was actually muted (autoplay requires muted), by syncing the
initial muted state from the media element in usePlayerControls.

Closes #88
2026-03-16 16:36:54 -04:00
Alex Gleason 7c83e42001 De-emphasize author on list/follow-pack cards in feeds and embeds
For kind 30000 (lists) and 39089 (starter packs), replace the prominent
avatar + bold name header with a content-first layout: the list title,
description, and member avatars are shown as the hero, followed by a
subtle small author attribution line (tiny avatar, muted text, timestamp).

This applies to both normal and threaded layouts, as well as compact/embed
mode used in quote posts and inline embeds.
2026-03-15 02:39:19 -05:00
Alex Gleason 0ffe2de89d Use short number format for large stats (e.g. 1.7K, 314K, 2M) 2026-03-14 02:39:14 -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 0781b1a87c Merge origin/main and re-apply avatar shape changes to conflicted files 2026-03-13 17:23:30 -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 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
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
Derek Ross 6acc6c4a0c Add list detail page, interests (NIP-51), and fix lists bugs
- Fix avatar overlap on /lists page (widen container + overflow-hidden)
- Fix dialog scroll in AddToListDialog and ListDetailDialog (flex flex-col)
- Add ListDetailPage with Feed/Members tabs at /:naddr route
- Add AddMembersDialog with NIP-50 profile search
- Add useInterests hook for NIP-51 kind 10015 hashtag interests
- Add follow/unfollow hashtag button on /t/:tag pages
- Add followed hashtag tabs in home feed
- Distinguish kind 30000 (List icon) from 39089 (PartyPopper) in NoteCard
- Filter deprecated d-tags (mute/pin/bookmark/communities) from kind 30000
- Add description/image support to UserList interface
- Support viewing and cloning other users' lists via naddr
- Fix list deletion (empty replacement + kind 5 + optimistic cache removal)
- Add share button to copy list naddr link
2026-03-08 17:41:07 -04:00
Mary Kate Fain be1771e4e5 Only show Share button on mobile
On desktop the URL bar and 'Copy Link' in the More menu already
cover sharing. The native share sheet is the unique value on mobile,
so hide the button at the sidebar breakpoint (900px+).
2026-03-07 13:38:38 -06:00
Mary Kate Fain a45c5c6647 Add Share button to posts, post detail, and profiles
Add a dedicated Share button using the Web Share API with clipboard
fallback across three locations:

- NoteCard action bar (between Zap and More)
- PostDetailPage action bars (both compact and expanded views)
- ProfilePage header (between More and Zap buttons)

On mobile and supported desktop browsers, tapping Share opens the
native OS share sheet with all installed apps (Twitter, WhatsApp,
etc.). On unsupported browsers, falls back to copying the link to
clipboard with a toast confirmation.

Closes #39
2026-03-07 13:29:08 -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
Alex Gleason 729a844606 Fix ESLint error: remove unused isTreasure variable in NoteCard 2026-03-04 14:46:49 -06:00
Chad Curtis f3ce89bc94 Consolidate action headers into generic EventActionHeader with KIND_HEADER_MAP lookup 2026-03-04 03:15:15 -06:00
Alex Gleason f27ca27d5f Fix crash when p tags have undefined pubkey values
Filter out undefined/empty pubkeys when extracting reply-to authors
from event p tags. Some events have malformed p tags without a pubkey
value, causing nip19.npubEncode to throw 'hex string expected, got
undefined'. Added filtering in both NoteCard (source) and ReplyContext
(defensive guard).
2026-03-03 17:24:17 -06:00
Derek Ross 52e20939c9 Fix lint errors: remove unused WebxdcEmbed import and DEV_KINDS constant 2026-03-03 16:10:06 -05:00
Derek Ross e6b0ccf9f1 Redesign development feed cards with NostrHub-style layout and Shakespeare integration
- Remove kind 31733 (AppSubmissionCard) from development feed
- Redesign GitRepoCard, PatchCard, PullRequestCard, CustomNipCard with
  labeled sections (Tags, Clone, Kinds, Labels), pill-shaped action buttons
- Detect Shakespeare apps (t=shakespeare + web tag) and show favicon, 'Open
  App' button, and 'Edit with Shakespeare' clone link
- Add full detail views: markdown rendering for custom NIPs and PRs,
  syntax-colored diffs for patches, commit/branch metadata display
- Fix development feed to query all dev kinds (30617, 1617, 1618, 30817)
- Route /dev -> /development, remove FAB, add per-kind page titles
2026-03-03 14:39:12 -05: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
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 600841705f fix: populate OS media controls with event image, title, and artist at all player call sites 2026-03-03 06:13:47 -06:00
Alex Gleason 28507550de Restore border-b to NoteCard, remove compensating divide-y from containers
The original border-b border-border was removed from NoteCard in 9bd646b
for a photos overlay feature that no longer uses NoteCard. This forced
every container to add divide-y divide-border as a workaround, but
several pages were missed (Bookmarks, Events, Trends, Search, etc).

Restore NoteCard's self-bordering and remove the now-redundant divide-y
wrappers. Existing overrides (EmbeddedNaddr !border-b, Notifications
border-0) continue to work correctly.
2026-03-02 12:00:13 -06:00
Chad Curtis 3868eaf1c3 Revert "Memoize NoteCard to prevent re-renders when feed state changes"
This reverts commit 364e95873d.
2026-03-02 04:47:41 -06:00
Chad Curtis 364e95873d Memoize NoteCard to prevent re-renders when feed state changes 2026-03-02 03:54:31 -06:00
Chad Curtis b465747573 Remove rounded-lg from NoteCard article wrapper 2026-03-02 02:46:30 -06:00
Chad Curtis 9bd646b160 Photos overlay: vine-style layout with swipe, comments modal, and shared action buttons 2026-03-02 01:33:58 -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
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 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
Alex Gleason 774db2798c Merge branch 'main' of gitlab.com:soapbox-pub/ditto 2026-03-01 21:27:20 -06:00
Chad Curtis c8d198108f videos: horizontal scroll rows, fixed short thumb sizes, fetch 12/page 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
Alex Gleason b59b34a5c6 Fix reaction card alignment: center content against emoji bubble and use size-6 avatar
Add min-h-10 and flex items-center on the content side so the text line
vertically centers against the size-10 emoji bubble. Bump inline avatar
from size-5 to size-6 for better visual balance.
2026-03-01 20:29:36 -06:00
Alex Gleason 4774f0f079 Revert "Use avatar with emoji badge overlay for reaction cards in threads"
This reverts commit f58a0ceed4.
2026-03-01 20:28:17 -06:00
Alex Gleason f58a0ceed4 Use avatar with emoji badge overlay for reaction cards in threads
Replace the standalone emoji bubble with the user's normal avatar plus a
small reaction emoji badge at the bottom-right corner. This keeps the
avatar column aligned with other threaded posts while still making the
reaction visually distinct.
2026-03-01 20:18:10 -06:00
Alex Gleason 07a25874ae Render kind 7 reaction events as compact activity cards instead of posts
Reaction events now display with a prominent emoji bubble, small inline
avatar, author name, and 'reacted' label — making them visually distinct
from regular posts in both thread ancestors and standalone detail views.
2026-03-01 20:08:46 -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
Mary Kate Fain c41405a0ee Show reply context on self-replies in feed
When replying to your own post, the compose box excludes your pubkey
from p tags. This left self-replies with no 'Replying to' context,
making them look like top-level posts. Now falls back to the parent
e-tag author hint or the event author itself so self-replies always
show thread context.

Closes #3
2026-03-01 12:58:14 -06:00