Introduce queryAll, a portable helper that exhausts a Nostr filter by
paging with the until cursor, capped at 5,000 events / 10 pages so
worst-case cost stays bounded. Works against any relay regardless of
its internal page size.
Migrate useCommunityMembers and useCommunityActivityFeed so membership
and moderation state are complete for any community that fits within
the cap, instead of silently truncating at 500 events.
Extract isAuthorizedAward helper as the single source of truth for
membership award validation, used by both resolveMembership and
useMyCommunities. Simplify resolveCommunityModeration by dropping
the dead banned-reporter guard from pass 1 (impossible under strict
rank ordering). Flip useMembersOnlyFilter default to opt-in to match
the spec's MAY wording, and reword the NIP to match.
- Rename tentative label to 'Interested' (Facebook-style, Star icon)
- Auto-enroll event authors as 'accepted' when publishing
- Let authors change their own RSVP from the detail page
- Restyle RSVP section to match About/Attendees headers
- Remove optional note field; click a button to submit immediately
- Move Attendees above RSVP
Places a NIP-51 kind 10004 bookmark button between the edit and share
buttons so users can save a community while viewing it, not just from
the feed card's more-menu.
Bookmarking a kind 34550 community now writes to the NIP-51 Communities
list (kind 10004) keyed by the addressable coordinate, so the reference
stays valid across community updates. My Communities merges bookmarked
communities as a third discovery source alongside founded and member-of,
with Founder/Member/Bookmarked badges on each card.
Bookmark toasts live on the mutation itself so they survive the more-menu
dialog unmounting between .mutate() and publish resolution.
- CreateCommunityDialog now only publishes kind 34550 (name, image, description)
- New AddMemberDialog on the community detail page handles membership:
- Founder can add moderators and members
- Moderators can add members only
- Badge definition (kind 30009) created lazily on first member add
- Community definition republished once with all changes batched
- Kind 8 badge awards published for each member
- Add Members button on Members tab, visible to rank 0 users
- Search dropdown moved outside ScrollArea to prevent clipping
- Add CreateCommunityDialog with name, image upload, description, and moderator type-ahead search
- Publish kind 30009 badge definition + kind 34550 community definition with d-tag collision check
- Context-aware FAB on My Communities tab opens the create dialog
- Default Search page tab to Communities instead of Posts
- Add Search to default sidebar order for new accounts
- Improve empty states on both Activities and My Communities tabs to guide users toward discovery
NoteCard's reaction/repost/zap/poll-vote branches return early with their
own ActivityCard layouts, skipping the inline 'X reposted' header that
the normal note layout renders below. As a result, when one of these
events appeared in a feed via a kind 6/16 repost, the reposter
attribution was silently dropped.
Add an optional `header` slot to ActivityCard and pass the repost header
into all four early-return branches when `repostedBy` is set.
Stacked avatars in PeopleAvatarStack are now clickable, navigating to
the user's npub profile so readers can jump straight to a member from a
follow list, follow set, or follow pack — not only from the surrounding
post. Each avatar is wrapped in a Link with a stopPropagation handler so
the click doesn't bubble up to the card-level navigation, and the focus
ring is now visible on keyboard focus.
Kind 3 follow-list events are legacy replaceable kinds (NIP-01) but
fell outside the 10000–19999 range that NoteCard, EmbeddedPeopleListCard,
PostDetailPage, and NoteMoreMenu all special-cased — so clicking a
follow list in a feed went to a per-event nevent that pinned to a stale
revision instead of the stable naddr. The four call sites are now
unified behind a new lib/encodeEvent.ts helper that treats kinds 0, 3,
and 41 as replaceable, alongside 10000–19999 and 30000–39999. The same
helper exposes encodeEventNevent for callers that intentionally want to
reference a historical version (e.g. the profile-recovery dialog).
Lightning (kind 9735) and on-chain (kind 8333) zaps now share a single
"zap" group bucket in the notifications page and render with the same
header, sats label, and Zap icon. The Zaps preference toggles both kinds
together; native and push notification queries pick up 8333 automatically
through the shared kind list.
Position the Restore button (or Current badge) absolutely in the
top-right corner of the embedded post, replacing the row beneath it.
Each snapshot now occupies just one container's worth of vertical
space, and the action sits next to the content it acts on instead of
detached below it. The overlay stops click and keyboard propagation so
clicking Restore doesn't also navigate away to the embedded card's
link target.
The double-container effect — outer card frame around an embedded post
that already had its own border — wasted vertical space and made each
snapshot read like two stacked boxes. Drop the outer frame so the
embedded card is the only container, with a primary-colored ring on the
current version. The redundant date row also goes (the embedded card
displays its own timestamp), leaving just a right-aligned Restore button
or 'Current' badge below each snapshot.
The note 3-dots menu now exposes a 'Restore previous version' option for
replaceable and addressable events the current user owns, sitting next
to Delete. It opens a generic EventRecoveryDialog modeled after the
existing profile/mute-list/badge recovery dialogs — querying past
versions with nostr.req() (to bypass NPool's NSet deduplication) using
the same (kind, authors[, #d]) filter shape, and rendering each
historical version through EmbeddedPost so any kind displays correctly.
Restoring republishes the chosen snapshot's content and tags via
useNostrPublish with the snapshot passed as 'prev' so published_at is
preserved. Inline isAddressableKind helpers in useDeleteEvent and
useNostrPublish are now sourced from a shared src/lib/eventKinds.ts.
The deletion event for a NIP-37 draft wrap (kind 31234) only carried
the addressable `a` coordinate. Per NIP-09, a deletion should also
reference the specific event by id when available, so relays and
clients that key their deletion logic on `e` tags don't miss it.
Look up the draft's event id from the TanStack Query cache (the drafts
list already stores it as `eventId` when parsing the wrap) and append
an `e` tag alongside the existing `a` tag. Falls back gracefully to
`a` only if the event id can't be resolved.
Regression-of: e93c6651
- Add aria-label to close button and companion selection buttons
- Replace flex-wrap with horizontal scroll (max-w-[18rem]) so only ~5
blobbis are visible at once; overflow scrolls horizontally
- Add visible thin scrollbar (.scrollbar-thin utility) overriding global
scrollbar-hiding, plus a right-edge fade gradient to hint at overflow
- Add flex-shrink-0 to prevent items from collapsing
- Break BlobbiWidgetContent destructuring across multiple lines for
readability
The on-chain zap kind used to fall through the "unknown kind" path in
every surface except InteractionsModal: a bare NIP-31 alt-tag tombstone
on PostDetailPage, a generic embedded preview for nostr: quote URIs,
and a plain "This event kind is not supported" string inside the reply
composer's parent preview. Visually it was nothing like a zap.
Route kind 8333 through dedicated cards that mirror the 9735 Lightning
treatment pixel-for-pixel: amber bolt bubble, sender avatar, "zapped"
verb, amber sats amount, italic comment. Per NIP.md we verify the
claimed amount against mempool.space before displaying it, so the new
`useVerifiedOnchainZap` hook short-circuits to the single-event path of
`verifyOnchainZap`. Until verification resolves (or if it fails) the
card shows a muted "verifying…" / "unverified" hint next to the amount
so we don't silently lie.
Covers three surfaces in one pass:
- Detail page (nevent URL): new isOnchainZap branch in PostDetailPage
- Embedded quotes: new EmbeddedOnchainZapCard in EmbeddedNote
- Reply composer parent preview: uses EmbeddedPost -> EmbeddedNote,
so it inherits the fix for free.
The Zaps tab only rendered NIP-57 receipts (kind 9735), so a post that
had been zapped only on-chain appeared to have no zappers at all. Merge
the two rails into a single unified view-model and render them with
identical rows — same avatar, same name line, same amber amount badge,
same chevron link. The dedup and on-chain verification are already
handled by useOnchainZaps upstream; this change is just plumbing.
The modal now takes the full target event instead of a bare eventId so
the on-chain query can compute the `a` coordinate for addressable kinds.
Updated all call sites (PostDetailPage, PodcastDetailContent,
MusicDetailContent) accordingly.
Previously the bolt icon next to reply/repost/react was stateless: an
outlined zap icon whether you'd zapped the post or not. This matched
neither the repost button (flips to the accent color when reposted) nor
the reaction button (fills when you've reacted), and the gap was most
noticeable with on-chain zaps where users expected the same visual
confirmation they get for Lightning.
Add a useUserZap hook that consults both rails in one REQ: kind 8333
filtered by authors+#e (our on-chain zap, self-authored), and kind 9735
filtered by #e with a client-side extractZapSender match (NIP-57
receipts are authored by the LNURL server, not the zapper). The send
hooks (useOnchainZap, useZaps) optimistically set the cache to true on
success so the icon fills immediately, without waiting for the relay to
echo the event back.
Wired into every action-bar renderer that carries the zap button:
NoteCard, PostActionBar, PhotoBottomBar, VinesFeedPage, BookFeedItem.
Remove the auto-close progress bar, the "Sent via Bitcoin/Lightning" rail
indicator, and the sats subtext under the USD amount. The screen now
dismisses only via the Done button, so the rail-specific plumbing
(autoCloseMs, kind) is gone from the component API as well.
Regression-of: 5c2c3513