Commit Graph

224 Commits

Author SHA1 Message Date
Alex Gleason 3e31d26660 Replace Twitter-style action bar with GoFundMe-style chip row
The campaign page (and every post feed) inherited Ditto's spread-out
pill action bar — icons across the full width, framed by a heavy
top+bottom border band, with cascading dots on the 'Show more replies'
thread connector. It read as a Twitter toolbar, which is wrong for a
fundraising client.

Restyle the shared action row across PostActionBar and NoteCard:
- Chip-style buttons (h-9 px-3 rounded-full) with inline counts.
- Engagement actions cluster left, share/more pushed right with a
  flex spacer instead of justify-between.
- Drop the unconditional top+bottom border band; pages add their own
  separator via className when needed.
- Show 'React'/'Reply'/'Repost' word labels on sm+ when the count is
  zero, so the bar reads as labelled affordances rather than icon
  pills floating in space.
- Add a 'chip' variant to ReactionButton so it can switch between the
  legacy pill (still used by PhotoBottomBar, PodcastDetailContent,
  MusicDetailContent, BookFeedItem, PostDetailPage, etc.) and the new
  chip look.

ThreadedReplyList's 'Show N more replies' button drops the four
cascading dots in favour of a single soft connector that brightens on
hover.

On the campaign page itself, wrap the engagement stats + action bar
in a soft card, add a 'Comments & donations' section heading with a
count, and replace the bare 'No comments yet' line with a dashed
empty-state CTA that opens the reply composer.
2026-05-18 22:47:18 -05:00
lemon 7dbfc31f04 Render zaps with comment card layout 2026-05-17 17:40:21 -07:00
lemon fb6f157c42 Align zap cards with comments 2026-05-17 17:40:21 -07:00
lemon 3504a24be5 Realign zap card with the regular comment layout
The zap card was an ActivityCard with a chunky amber circle in
the avatar slot and a compact ActorRow up top. That visual
language reads as 'an activity log entry' which clashes with the
NIP-22 comments alongside it on the campaign page. Rebuild it
to mirror NoteCard's normal layout exactly:

- Donor avatar takes the standard size-11 (size-10 in threaded
  mode) slot, with a small amber zap badge anchored bottom-right
  to keep the kind signal.
- Author block uses the same font-bold name + nip05 + timeAgo
  stack as a regular note, with the verb ('donated' / 'sent')
  and amount inlined on the name row so the card reads as one
  sentence.
- For campaign targets the amount is followed by 'to <Campaign
  Title>' where the title is a clickable Link to the naddr —
  same routing the regular CommentContext header would use.
  Resolved via a single useAddrEvent call gated on the receipt's
  a-tag so non-campaign zaps incur no fetch.
- The donor comment renders below the author row as muted italic
  text — same spot a normal note's body sits — instead of being
  tucked under the actor row.
- Action bar uses the shared {actionButtons} JSX with the exact
  same spacing as a comment, so reply / repost / react / zap /
  share / more line up vertically across cards in the thread.

The amber Zap import and ProfileHoverCard / Avatar imports were
already in scope, so no new imports beyond useAddrEvent.
2026-05-17 17:40:21 -07:00
lemon f49c20787e Polish zap cards and clamp the campaign story
Four related changes:

1. Campaign story now clips to three lines (~4.5rem) behind a
   soft fade overlay, with a Read more / Show less toggle.
   When there's no story yet the empty placeholder renders
   unclipped as before.

2. Zap cards in NoteCard gained the same reply/repost/react/
   share action bar as a regular note. Each kind 9735 / 8333
   event is a valid Nostr event in its own right, so NIP-22
   replies target it directly via its event id and reactions
   bind to it the same way.

3. The 'zapped' label is now 'donated' when the receipt's a-tag
   points at a kind 30223 campaign, and 'sent' otherwise. The
   target kind is read from the addressable coordinate; pure
   e-tag Lightning zaps fall back to 'sent' without a fetch.

4. Zap amounts render in USD when a BTC→USD price is cached,
   with the raw sats string moved to the title tooltip. Falls
   back to sats when the price hasn't loaded. A new useBtcPrice
   hook shares the existing 'btc-price' cache key so all
   call sites (NoteCard, CampaignCard, useBitcoinWallet) dedupe
   to one in-flight request.
2026-05-17 17:40:21 -07:00
lemon 760e11138d Show donation receipts in campaign comments
Three changes work together so campaign pages reflect how
campaigns actually receive support — via on-chain donations,
not Lightning zaps:

1. NoteCard's zap-receipt layout now renders kind 8333 in
   addition to kind 9735. The helpers (getZapAmountSats,
   getZapSenderPubkey) already branched correctly; only the
   isZap gate and the amount/message extraction were 9735-only.

2. PostActionBar gained a hideZap prop. Campaigns set it so the
   action bar shows only reply / repost / react / share — a
   generic Lightning zap is the wrong CTA when the campaign
   has its own donation flow.

3. CampaignDetailPage interleaves kind 8333 donation receipts
   into the comments thread, sorted by created_at alongside
   kind 1111 comments. Each donation produces one receipt per
   beneficiary, so we dedupe by (txid, donor) and rewrite the
   canonical receipt's amount tag to the summed total so the
   card shows the full donation rather than one share.
2026-05-17 17:39:39 -07:00
lemon b5f2d9bebb Simplify country flag card backdrop 2026-05-16 11:04:18 -07:00
lemon 343085684e Adapt country flag cards for light mode 2026-05-16 10:58:15 -07:00
lemon bc0b8f83d4 Render actions as feed cards 2026-05-16 09:00:02 -07:00
Chad Curtis 4fec16c281 Drop flag emoji fallback; render flag-color gradient instead
The giant blurred emoji fallback never quite matched the eventual
Wikipedia flag image visually, so when the network was slow the card
would visibly swap from emoji to image. Replace the fallback with the
sampled flag-color gradient (already used by the country pill via
useFlagPalette) which matches the upcoming image's color palette
closely and shares the same opacity/mask shape — so the eventual swap
to the real flag image is seamless.

Also wires the flag-mode foreground treatment through both NoteCard
layouts (normal + threaded): when the backdrop is active, the action
header + author row pick up white text with a strong text-shadow for
readability against the dark wash. The country pill stays scoped out
of that flip so it keeps its own gradient surface.

Includes prior tweaks from this arc: full-width flag banner, taller
backdrop area (h-64 / h-72), mask-image bottom fade, increased dark
wash for text contrast, original (not thumbnail) Wikipedia source for
sharpness, no blur on the image, lighter drop-shadow on the pill's
flag emoji.
2026-05-15 23:51:24 -05:00
Chad Curtis 5a60bf7b8c Lay a fading flag emoji behind country-rooted posts
Country-rooted kind-1111 cards now wear a giant blurred flag emoji
anchored upper-right, fading through hsl(var(--background)) before it
reaches the post body. Echoes the country detail hero's
'background + linear-gradient fade' technique, scaled down for a card.

The gating logic for the pill and the backdrop is factored into a
shared useCountryRootContext hook so they always appear or disappear
together. NoteCard's <article> picks up 'relative isolate' so the
absolute backdrop stays contained inside the card it belongs to.
2026-05-15 23:25:20 -05:00
Chad Curtis e5a74a85f6 Promote country pill into a glassy passport stamp
The flat tinted chip didn't sell 'this is a country-level post'. Replace it
with a gradient pill (primary -> accent) carrying a white flag disc, a
'POSTED FROM' tracker label, and a soft primary-tinted shadow that
intensifies on hover. The country preview popover also picks up a matching
gradient accent bar.

Pill stays anchored to the upper right of NoteCard's header and hides
inside its own country feed, matching the previous visibility rules.
2026-05-15 23:09:41 -05:00
Alex Gleason 2fc7a9ac41 Fix left sidebar collapse and kind-3 follow list rendering
Two post-merge regressions from the ditto/main merge:

1. Left sidebar collapsed to icon-only column at desktop widths.

   Ditto's responsive aside (`hidden sidebar:flex ... lg:w-1/4
   lg:max-w-[300px]`) sizes itself off the flex parent. Agora wraps
   `<LeftSidebar />` in an extra `<div className="hidden
   sidebar:block">`, which had no width — so `w-1/4` computed
   against zero and the sidebar collapsed.

   Removed the wrapper div in MainLayout — the `<aside>` now handles
   its own hiding and width directly inside the flex parent, matching
   Ditto's structure.

2. Kind 3 contact lists rendered as empty/broken cards.

   Ditto unified kinds 3/30000/39089 under `PeopleListContent` (uses
   `parsePeopleList` which synthesizes a "{Name}'s follows" title
   for kind 3 since the event carries no title/description/image of
   its own). Agora's NoteCard still used the old `FollowPackContent`
   path that only matched 30000/39089, so kind 3 fell through to the
   default note render.

   Swapped `FollowPackContent` for `PeopleListContent` and added
   kind 3 to the `isFollowPack` check.

Regression-of: 740fc1c6
2026-05-13 19:33:37 -05:00
lemon 5cf1157636 Rename fundraising goals to goals 2026-05-11 11:12:13 -07:00
sam e12716722a remove shape stuff 2026-05-11 14:49:11 +07:00
lemon 9045ff3c41 Harden fundraising goal zaps 2026-05-02 23:40:05 -07:00
lemon 09e211f48a Render fundraising goals via NoteCard for moderation support
Replace standalone GoalCard with NoteCard in the community fundraising
tab so goals get the same '...' menu with remove/ban actions that
comments have. Strip GoalCard down to just the compact inline renderer
(no variant prop, no skeleton, no card-only imports). Simplify
useCommunityGoals to return plain events instead of parsed wrappers.
2026-05-02 23:40:05 -07:00
lemon 27b60b2a6f Refactor goal components: deduplicate GoalCard/GoalContent, fix staleness
- Unify GoalCard and GoalContent into a single component with variant prop
- Extract useGoalDisplay hook for shared display logic (author, progress,
  community link, deadline, image)
- Add useNow(60s) interval so deadline labels refresh automatically
- Add generic parseATagCoordinate utility to nostrEvents.ts
- Replace DOM-mutating image onError with React state
- Remove dead isGoalFunded export and redundant created_at in publish
- Delete GoalContent.tsx (-144 net lines)
2026-05-02 23:40:05 -07:00
lemon d1017697a4 Add NIP-75 community fundraising goals
Implement zap goals (kind 9041) linked to communities via a-tag.
Includes goal creation dialog, progress tracking from zap receipts,
recipient profile/lightning address display, community link, and
members-only filtering. Goals appear in community detail Fundraising
tab, activity feed, and main feed via NoteCard.
2026-05-02 23:39:07 -07:00
sam b46703eaed remove blobbis 2026-04-30 13:19:22 +07:00
sam 3f28bf571a remove all theme stuff 2026-04-29 22:04:39 +07:00
lemon e21ee2e4fc Enforce report pubkey match and add members-only filter toggle
Two NIP-alignment fixes:

Gap 1 — Report warnings now require `p` match (correctness).

Previously `CommunityContentWarning` looked up reports by event id only,
so any community member could publish a kind 1984 pairing a victim
event's id with their own pubkey on the `p` tag to force a warning
overlay onto an arbitrary event. Added `getApplicableReports` in
communityUtils mirroring `hasApplicableContentBan`, and use it to
require `report.targetPubkey === event.pubkey` before the warning
renders. Matches NIP.md §Reports — Content Warnings: "report warnings
MUST only attach to content when the target event's id matches the
report's `e` tag and the target event's pubkey matches the report's
`p` tag."

Gap 2 — Members-only filter toggle.

The NIP recommends canonical community feeds discard non-member
content by default. Added a shield-icon toggle that controls this as
a presentation-layer filter, defaulting on. When active, community
feeds (Activities feed, per-community Comments tab, and any future
community-scoped content surfaces) only show events authored by
chain-validated members. When off, everything scoped to the
community is shown regardless of authorship.

- `useMembersOnlyFilter` — localStorage-backed hook with cross-tab
  sync; one preference shared across all community surfaces.
- `MembersOnlyToggle` — shield / shield-off icon button with tooltip
  explaining current state.
- Filtering is applied post-query in the consumer pages, so toggling
  is instant and doesn't invalidate the query cache.
- Community definition events (kind 34550) are never filtered — they
  represent the community itself, not user-generated content.
- Toggle placement: in `CommunitiesPage` header (scopes the global
  Activities feed); in `CommunityDetailPage` alongside the tabs
  (scopes every content feed in that community, now and future).
- Empty-state copy hints at the filter when a list is empty only
  because of it.
2026-04-27 09:22:08 -07:00
lemon 864057f382 Tighten community moderation performance and unify context paths
- Extract community content warning's context subscription into the
  wrapper itself so NoteCard's memo() boundary no longer depends on
  moderation data. Refetches now re-render only the warning and the
  three-dot menu, not the whole card.
- Rename useCommunityModeration -> useCommunityModerationForEvent and
  return the full context value; PostDetailPage installs it as a
  Provider, removing 7 manual communityContext prop passes. Unifies the
  three previous paths for computing CommunityMenuContext down to one.
- Seed the per-community members cache from the activity feed so
  opening a community detail page after the feed loads hits warm cache
  instead of re-querying kind 8 awards and kind 1984 reports.
- Single-pass parse in resolveCommunityModeration (was parsing each
  kind 1984 event twice across the ban and report passes).
- Drop the redundant A-tag existence check in parseCommunityReport;
  callers scope events via the relay's #A filter.
- Scope ban/report cache invalidation with a predicate that only
  matches activity feeds containing the affected community's A tag.
- Drop CommunityMembership.totalCount (was just members.length) and
  consolidate scattered EMPTY_* sentinels into EMPTY_MEMBERSHIP and
  EMPTY_RANK_MAP in communityUtils.
2026-04-27 09:22:08 -07:00
lemon fec7021a7f Implement community moderation: reporting, content bans, and member bans
Add two-tier moderation system for hierarchical communities using kind 1984
events scoped via A tags. Authoritative bans use NIP-32 labels
([l, ban, moderation]) and require rank authority. Soft reports use standard
NIP-56 types and trigger content warnings for any valid member.

- Update NIP.md with ban/report classification, NIP-32 label schema, and
  reinstatement via kind 5
- Add parseCommunityReport(), resolveCommunityModeration() to communityUtils
- Update resolveMembership() to apply moderation overlay (remove banned members)
- Update useCommunityMembers to fetch kind 1984/5 and resolve moderation
- Add CommunityModerationContext for propagating moderation state
- Add CommunityReportDialog for soft reports (NIP-56 types)
- Add BanConfirmDialog for content removal and member bans with optional reason
- Add CommunityContentWarning component for click-to-reveal reported content
- Wire moderation into NoteMoreMenu (auto-detects community context)
- Wire moderation into CommunityDetailPage (member ban buttons, feed filtering)
- Add Remove content / Ban @user menu items to NoteMoreMenu
- Remove Copy Link to Post and Mention @user from NoteMoreMenu
- Move Mute Conversation into the mute/report section
2026-04-27 09:22:07 -07:00
lemon c82c6f4179 add communities page with NIP-72 hierarchical community support 2026-04-19 17:42:31 -07:00
sam ced5d00163 ported actions/challenges 2026-04-18 13:38:50 +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 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 c1ec7a25ed Use published_at tag to show created/updated verbs in event action headers
Replaceable and addressable event headers now distinguish between
first publish and subsequent updates using the published_at tag:
- published_at == created_at → 'created' verb (e.g. 'created an emoji pack')
- published_at != created_at → 'updated' verb (e.g. 'updated an emoji pack')
- no published_at → 'shared' fallback for backward compatibility
2026-04-05 15:12:19 -05:00
Chad Curtis 946be28b81 Use standard author layout for follow pack/set cards instead of content-first 2026-04-05 10:59:01 -05:00
Chad Curtis 89250c7472 Add kind action headers for follow packs and follow sets 2026-04-05 10:56:22 -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 00c801e9dc Add poll voter interactions, kind 1018 vote rendering, and DRY activity card refactor
Poll voters:
- Clickable voter avatar stack + vote count on polls (before and after voting)
- Voters modal showing each voter with avatar, name, option, and nevent link
- Extract VoterAvatarsButton to DRY the avatar stack pattern

Kind 1018 vote rendering:
- Register in PostDetailPage as compact activity card with parent poll ancestor
- Register in NoteCard with threaded + normal variants (user avatar, not icon)
- Register in CommentContext with Vote icon, 'a vote' label, and rich hover showing voter + option
- Extract usePollVoteLabel hook to DRY vote label resolution across 3 call sites

ActivityCard refactor:
- Extract shared ActivityCard and ActorRow from NoteCard
- Refactor reaction (kind 7), repost (kind 6/16), zap (kind 9735), and poll vote (kind 1018)
- Reuse ActivityCard in PostDetailPage for vote detail view
- Net ~250 line reduction in NoteCard
2026-04-04 03:09:20 -05:00
Alex Gleason 469806886a Fix card navigation firing on button/link clicks in NoteCard 2026-04-03 17:45:36 -05:00
Alex Gleason 8e3f778f5b Improve Zapstore and app handler card display
- Rename Zapstore kind labels to include 'Zapstore' prefix across all
  label registries (NoteCard, PostDetailPage, CommentContext,
  ExternalContentHeader, NotificationsPage, extraKinds)
- Wrap Zapstore (32267, 30063, 3063) compact and detail content in
  rounded bordered cards with hover effects; remove redundant mt-2/mt-3
  margins from component roots
- Replace useLinkPreview thumbnail with metadata banner/picture in kind
  31990 app handler cards (compact and full views)
- Add pt-4 to Zapstore detail card wrappers in PostDetailPage
- Fix sticky tab bar (SubHeaderBar z-10) being painted over by card
  content: remove z-10 from AppHandlerContent inner div and add z-0 to
  the main content column in MainLayout
2026-04-03 16:20:40 -05:00
Alex Gleason 3e429fe0b0 Add rendering for Zapstore release (kind 30063) and asset (kind 3063) events
- New ZapstoreReleaseContent component: shows app icon/name fetched from the
  linked kind 32267, version badge, channel badge, release notes, and a
  downloads section that fetches and renders each linked kind 3063 asset
- New ZapstoreAssetContent component: shows MIME-type icon, platform/arch
  badges, file size, SHA-256 hash, commit hash, supported NIPs, and APK
  certificate hashes
- Register both kinds in NoteCard, PostDetailPage, extraKinds, CommentContext,
  ExternalContentHeader, and NotificationsPage label/icon maps
- Route kind 3063 to the Zapstore relay in NostrProvider and useEvent
- Kind 3063 is excluded from feeds (display-only on direct navigation)
2026-04-02 23:09:01 -05:00
Alex Gleason 24a645277e Fix custom emoji stretching by adding object-contain to all emoji images
Custom emoji images with non-1:1 aspect ratios were being stretched
into a square. Added object-contain to preserve natural aspect ratio
within the bounding box. Moved text sizing classes to parent containers
for reaction emoji bubbles so unicode emojis still size correctly.
2026-04-02 03:50:50 -05:00
Alex Gleason f4e38123e4 Add display for kind 31990 NIP-89 app handler events 2026-04-02 01:00:32 -05:00
Alex Gleason 73bf03cfab Add Blobbi view link and register kind 31124 in feed/detail views
Add a 3-dots menu to the Blobbi dashboard with a 'View Blobbi' link that
navigates to the naddr detail page. Register kind 31124 (Blobbi Pet State)
across all UI registration points so Blobbi events render properly in
feeds, detail pages, comment contexts, and embedded previews.
2026-04-01 19:01:47 -05:00
Chad Curtis 5887f790c6 Style expanded thread siblings with distinct connector line
Add threadedLineClassName prop to NoteCard to allow customizing the
connector line color. Revealed hidden siblings use bg-primary/30
to visually distinguish them from the main thread chain. Remove
bottom border from the expand thread button for seamless flow.
2026-04-01 03:46:04 -05:00
Alex Gleason 0469b6cec9 Display encrypted letters as interactive 3D envelopes with Nushu ciphertext
Register kind 8211 across the event rendering pipeline so encrypted
letters render as 3D interactive envelopes instead of raw ciphertext.
Back shows a sealed envelope with sender/recipient names in script font
and a wax seal avatar. Click flips the envelope (CSS 3D transform),
click again opens it to reveal the ciphertext rendered as Nushu
characters -- a real historical secret women's script from China.
2026-03-31 16:32:27 -05:00
Chad Curtis 081ad9240f fix: move zap comment inside right column instead of pl-[52px] offset 2026-03-31 08:19:47 -05:00
Chad Curtis b9b9363468 Render kind 9735 zap receipts and kind 0 profiles in feed and detail pages
- Kind 9735: feed and detail cards mirror the reaction card layout exactly — zap icon bubble, sender avatar/name, 'zapped N sat(s)', timestamp (ml-auto), message indented under profile on the line below. Threaded variant included. Zap rows in InteractionsModal now link to the receipt nevent. ZapEntry gains eventId field.
- Kind 0: feed card renders ProfileCard inline; detail page renders ProfileCard directly with no action header
- CommentContext: add kind 0 (profile) and 9735 (zap) to KIND_LABELS and KIND_ICONS
- NoteCard KIND_HEADER_MAP: add kind 9735 zap header
- shellTitleForKind: 'Zap' for 9735, 'Profile' for 0
2026-03-31 00:24:02 -05:00
Chad Curtis 11ecfb1bcf Render kind 9735 zap receipts and kind 0 profiles in feed and detail pages
- Kind 9735: feed card shows sender, amount (pluralized sat/sats), message; detail page shows activity-style card with ancestor thread; zap rows in InteractionsModal now link to the receipt nevent instead of sender npub, and include eventId on ZapEntry
- Kind 0: feed card renders ProfileCard inline; detail page renders ProfileCard directly with no action header
- CommentContext: add kind 0 (profile) and 9735 (zap) to KIND_LABELS and KIND_ICONS
- NoteCard KIND_HEADER_MAP: add kind 9735 zap header
2026-03-30 23:53:57 -05:00
Chad Curtis ef04de67c0 Improve notification rendering for badges and letters
- Add hideKindHeader prop to NoteCard, used by ReferencedNoteCard to
  suppress redundant action headers in repost/reaction/zap notifications
- Redesign badge award notification: show full BadgeContent showcase card
  with prominent rounded-pill Accept Badge button below
- Redesign letter notification: larger centered envelope (minimal mode
  hides name/timestamp), click opens LetterDetailSheet inline, View All
  Letters button below
2026-03-30 00:13:01 -05:00
Alex Gleason 646c95a86f Show kind action header on threaded ancestor NoteCards
The KIND_HEADER_MAP action header (e.g. 'updated their badges',
'created a badge', 'shared a photo') was only rendered in the normal
NoteCard layout. Now it also appears in the threaded layout, so parent
events shown as ancestors in reply threads display their kind context.
2026-03-29 14:56:55 -05:00
Alex Gleason e876e290da Encode replaceable events (kind 10000-19999) as naddr URLs
Replaceable events should use naddr encoding (kind + pubkey + empty
identifier) rather than nevent (event ID), since they are identified by
their coordinates. This fixes kind 10008 profile badge events linking
to /nevent1... instead of /naddr1... from the feed.
2026-03-29 14:45:45 -05:00
Alex Gleason 0df942cb9d Display kind 20 photo events in detail view and add Photo indicator
- Add isPhoto detection and PhotoDetailContent in PostDetailPage so kind 20
  events render their image gallery when viewed directly via nevent links
- Add parsePhotoUrls helper and ImageGallery import to PostDetailPage
- Add 'Photo' shell title for kind 20 loading state
- Add KIND_HEADER_MAP entry for kind 20 ('shared a photo') in NoteCard
- Add Photo attachment indicator in EmbeddedNote for kind 20 events in
  quote posts and reply context
2026-03-29 14:09:39 -05:00
Alex Gleason cf9d409166 Migrate profile badges from kind 30008 to kind 10008
Profile badges should be a replaceable event (kind 10008), not an
addressable event (kind 30008) with a fixed d-tag. This follows the
same deprecation pattern used by NIP-51 lists.

All writes now publish kind 10008. All reads query both 10008 and
legacy 30008, picking whichever is newest, for backwards compatibility
during the transition period.
2026-03-29 13:10:22 -05:00
Mary Kate Fain c3d7984d7a Fix inconsistent use of 'geocache' vs 'treasures' terminology
Closes https://gitlab.com/soapbox-pub/ditto/-/work_items/198
2026-03-28 12:44:28 -05:00