Commit Graph

71 Commits

Author SHA1 Message Date
mkfain af483d9989 Cut Approved from organization moderation, fix Featured load latency
Organizations now use a two-axis moderation model — featured and hidden.
The approval axis is campaign-only. Every Agora-tagged organization is
publicly visible by default; moderators curate by lifting orgs into the
Featured shelf or suppressing them with a Hidden label, nothing in
between.

Concretely:

- CommunityModerationMenu drops the Approve / Unapprove items. The org
  side never publishes those labels; useOrganizationModeration's
  mutate() rejects them defensively so a stray UI bug can't poison the
  label stream with axis-mixed events. The shared ModerationData type
  still tracks approvedCoords for symmetry with the campaign side, but
  the org UI never reads or writes it.

- /communities loses the in-flight 'Approved organizations' grid that
  was never finished and was conceptually wrong here. The moderator-only
  rail formerly called 'Pending review' is renamed 'Needs review' and
  re-derived as t:agora AND not featured AND not hidden.

Two perf fixes that should make Featured paint noticeably faster:

- The 200-event discovery query and the 2000-event label fold that
  power the moderator rails now live INSIDE ModeratorReviewSections.
  Non-moderator viewers (the overwhelming majority on /communities)
  never trigger either query — they used to fire at the page level
  whether they were used or not.

- Every CommunityMiniCard used to subscribe to useOrganizationModeration
  individually so it could overlay the Hidden badge and kebab. With
  18+ cards per page, that meant 18 component subscriptions to a
  query no non-mod cared about. The badge + kebab now live inside a
  single CommunityModerationOverlay that's gated on isMod up front
  and returns null for everyone else; non-mods never subscribe.

- staleTime on useOrganizationModeration and useFeaturedOrganizations
  bumped from 30s to 5m, with a 1-hour gcTime. Moderators feature and
  hide on human timescales, not seconds — repeat visits to /communities
  shouldn't pay a fresh relay round-trip every half-minute. Local
  changes still invalidate the keys explicitly so moderator actions
  show up immediately.

Also reverts two pieces of unrelated scope creep I built by mistake:
the 'Approved campaigns' section on the org detail page and the
moderator kebab on the campaign detail page. Those weren't asked for —
they were chasing a misread of an earlier instruction.

NIP.md updated to reflect the campaign-vs-org axis split and the new
'Needs review' surface name.
2026-05-21 21:43:48 -05:00
mkfain 9fd585ebdd Moderator-curated featured organizations
Replace the hardcoded FEATURED_ORGANIZATION_AUTHORS allowlist with the
same NIP-32 label flow that curates featured campaigns: Team Soapbox
pack members publish kind 1985 labels in the agora.moderation namespace
tagging an organization's 34550:<pubkey>:<d> coordinate as featured,
hidden, or approved, and useFeaturedOrganizations folds those labels
into the /communities Featured shelf.

The campaign and organization label streams share a single namespace
and a single moderator pack — they're separated purely by which kind
prefix the 'a' tag carries. To keep that contract enforced in one
place, the constants, types, and folding logic are now in
src/lib/agoraModeration.ts; useCampaignModeration and the new
useOrganizationModeration both call foldModerationLabels with their
respective kind. The campaign hook's external surface
(AGORA_MODERATION_NAMESPACE, ModerationLabel, CampaignModerationData)
is preserved via re-exports so existing call sites don't move.

Moderators see a CommunityModerationMenu kebab overlaid on every
CommunityMiniCard exposing approve/unapprove, hide/unhide, and
feature/unfeature. Mounting reads moderation state once per page from
the shared TanStack cache, mirroring CampaignCard. Non-moderators see
no overlay (the menu returns null) and no card affordances change.

The 'My organizations' shelf intentionally ignores moderation — a
user's own founded, moderated, or followed organizations always render
regardless of label state. Only the Featured shelf consumes the
curation rollup.

The Featured grid is uncapped: moderators control how many orgs
surface by labeling, and ordering follows the recency of each
'featured' label so re-publishing bumps an org to the top.

NIP.md's 'Campaign Moderation Labels' section is renamed to 'Agora
Moderation Labels' and documents the kind-34550 coord form and the
'My organizations ignores moderation' rule.

Note: existing surfaced organizations will disappear from the shelf
until a moderator publishes featured labels for them.
2026-05-21 21:12:33 -05:00
mkfain 7ee35644e3 Strict t:agora tagging for all Agora-created content
The Agora activity feed now filters strictly to Agora-created content via
the relay-indexed single-letter `t:agora` tag. Multi-letter tags like
NIP-89 `client` are not indexed by relays and cannot serve this purpose.

Every event Agora publishes that represents first-class Agora content
now carries `["t", "agora"]`, added via a new `withAgoraTag` helper
in `src/lib/agoraNoteTags.ts` that dedupes against any user-supplied
`t:agora` tag.

Tagged at publish time:
- Communities (kind 34550) — CreateCommunityPage
- Campaigns (kind 30223) — CreateCampaignPage, useArchiveCampaign
- Pledges (kind 36639) — CreateActionPage (alongside agora-action)
- Calendar events (kinds 31922 / 31923) — CreateEventPage and
  CreateCommunityEventDialog
- Onchain zaps (kind 8333) — useOnchainZap, useDonateCampaign,
  SendBitcoinDialog
- Zap goals (kind 9041) — CreateGoalDialog
- NIP-22 comments (kind 1111) — usePostComment, covering every comment
  authored from within the app regardless of root kind
- Kind 1 notes — already covered by ComposeBox default tags

Intentionally not tagged: reactions, reposts, follows, profile metadata,
lists, settings, badges, vanish requests, encrypted DMs, live chat.

useAgoraFeed tightened:
- Entity kinds and Agora-comment kinds now require `#t=agora` at the
  relay layer (server-side filter).
- World layer (kind 1111 / 1068 with `#k=iso3166|geo`) remains
  unfiltered — intentionally cross-client.
- `#Agora`-tagged kind 1 notes still surface from any author (preserves
  viral / opt-in discovery via user-typed hashtags).
- Donation enrichment now requires the Agora marker on zap receipts.
- `isRelevantAgoraEvent` rewritten as a strict checker that demands
  the marker for everything outside the world layer.

Legacy content without the marker disappears from the feed. It remains
reachable by direct link and via kind-specific directories (e.g.
`/campaigns/all`). Authors who edit a legacy event through the Agora UI
will automatically add the marker via the helper.

NIP.md updated with a new "Agora Content Marker" section under "Agora
Protocols" — documents the tagged-kind table, the untagged-kind list,
the canonical query shape, and the backward-compatibility behavior.
2026-05-21 20:25:01 -05:00
Chad Curtis d66eaf6aa4 Redefine Campaign kind: 30223 -> 33863 with wallet endpoint
Replaces the kind 30223 Campaign spec with a clean kind 33863 design.
Hard cutoff: no migration, no dual-read, no legacy support.

Key changes:
- Kind number 33863 (FUND on T9 keypad).
- Single `w` tag carries one bech32(m) wallet endpoint. Prefix
  selects the mode: `bc1q`/`bc1p` for public on-chain, `sp1` for
  silent payments (BIP-352). Other prefixes are rejected.
- Recipient `p` tags removed. Campaigns are self-authored; the
  event author is the beneficiary and owns the wallet in `w`.
- No split weights, no dust calculations, no BIP-340/341 Taproot
  derivation from Nostr pubkeys.
- `t` topic tags and `agora.category` labels removed. Discovery is
  via search, country (`#i`), and moderator curation.
- `image` -> `banner`, with required NIP-92 `imeta` for dim,
  blurhash, MIME, and SHA-256.
- `goal` is a single integer USD value, no unit, no currency code.
- `status` tag removed. To close a campaign, publish a NIP-09 kind
  5 deletion referencing the campaign's `a` coordinate.
- `location` legacy tag removed.
- Kind 8333 receipts for campaigns carry no `p` tags; verification
  matches tx outputs against the campaign's `w` address. SP
  campaigns publish no receipts and hide all aggregate UI by design.
2026-05-21 19:03:55 -05:00
Alex Gleason b53cb20d61 Replace Discover with Support nav link
Remove the Discover page and route entirely. In the top nav, swap the
Discover entry for a Support link pointing at /campaigns/all (the all
campaigns directory).

Also drops the now-orphaned DiscoverHero component and useDiscoverFeed
hook, and updates the NIP.md campaign moderation note that referenced
the deleted /discover route.
2026-05-21 17:39:15 -05:00
lemon ffb9c93ee6 Add pins across detail comments 2026-05-21 14:08:11 -07:00
lemon 48794fa3b4 Pin campaign activity updates 2026-05-21 14:08:11 -07:00
lemon f3c9a74b0f Simplify pledge creation form
Pledges are now open-ended by default — drop the optional start
date input and stop publishing the legacy `start` tag. The kind
36639 `created_at` already marks when the pledge becomes active.

Hide the built-in cover image templates so the upload area is the
only path for cover art, encouraging pledgers to supply their own
visuals. The thumbnail strip remains available via the
`templates` prop on `CoverImageField` for other surfaces.

Rename the description heading from "Story" to "Description"
to avoid implying a narrative expectation, and label the optional
tag input as "Recommended" to nudge categorization.

Mirror the same changes in `CreateActionDialog` (the community
scoped variant) and mark `start` as legacy in `NIP.md`.
2026-05-20 12:32:57 -07:00
lemon c9b48aeaae Align pledges with campaign patterns 2026-05-20 12:32:57 -07:00
lemon 3dd229edfb Reframe actions as pledges 2026-05-20 12:32:57 -07:00
mkfain 09bd4096e2 Make Featured a moderation axis and add an All Campaigns page
Featured was a hardcoded array of naddrs in src/lib/featuredCampaigns.ts.
Promote it to a third moderation axis (`featured` / `unfeatured`)
alongside `approved` and `hidden`, managed by Team Soapbox pack members
through the same kebab menu on each campaign card.

The Featured row on the homepage now:
- Reads from `moderation.featuredCoords`, ordered newest-featured-label first.
- Caps at 4 campaigns.
- Adapts its grid to 1/2/3/4 desktop columns based on count (mobile stays
  one column), collapses when nothing is featured, and surfaces the hero
  `variant="featured"` card only when exactly one campaign is featured.
- Hide still wins: a featured-then-hidden campaign disappears from the row.

Rename the homepage's second section from "All campaigns" to "Community
Campaigns", which more accurately reflects that it's the approved-not-
hidden set with featured campaigns deduplicated out.

Add a new /campaigns/all page that lists every campaign found on relays
(approved, pending, and unmoderated alike), with a "Show hidden" toggle
that adds hidden campaigns back in. The Discover page's "All campaigns"
link now points here instead of /, since the homepage is no longer a
truly-all view post-moderation. Also surface a small "Browse all
campaigns" link beneath the Community Campaigns grid so the new page is
discoverable from home.

Update NIP.md to document the third axis and the home-page surfacing
rules (Featured row, Community Campaigns grid, Discover shelf).

Delete src/lib/featuredCampaigns.ts entirely — there's no longer a build-
time list to maintain.
2026-05-19 18:04:28 -05:00
Chad Curtis d065580e47 Curate the campaigns homepage with Team Soapbox labels
Move campaign curation from a hardcoded HIDDEN_CAMPAIGN_COORDS set to a
real moderation system. Team Soapbox (kind 39089 follow pack
k4p5w0n22suf) is the moderator roster; each member signs NIP-32 kind
1985 labels in the agora.moderation namespace to approve or hide a
campaign. The home page and Discore shelf render the approved-and-
not-hidden set; moderators additionally see Pending + Hidden sections
and a per-card kebab menu. Non-moderator authors get a Your Campaigns
section explaining their campaign is live on Nostr but awaiting a
homepage approval.
2026-05-19 16:14:45 -05:00
Alex Gleason d71d6de05f Publish a single kind 8333 receipt per donation tx
A single Bitcoin transaction with N outputs now produces a single kind
8333 onchain-zap event listing every recipient under its own `p` tag,
instead of one event per recipient. The `amount` tag carries the total
sats paid to the listed recipients (the full donation, excluding the
donor's change).

This is straight-forward forward-compatibility: legacy single-recipient
events are just the degenerate case (one `p` tag, amount equal to the
one recipient's slice). Aggregators (`useCampaignDonations`,
`useGlobalDonations`) simplify to summing the `amount` tag across every
matching event — under both schemas an event's `amount` is the total
paid to the recipients listed in that event, so the sum across all
events for a campaign is the campaign total either way.

The verifier (`verifyOnchainZap`) now sums tx outputs paying any listed
recipient's derived Taproot address and strips the sender from the
recipient set so a tx that includes the sender plus legitimate
recipients still verifies. The notifications surface uses a new
`getZapAmountSatsForRecipient` helper to attribute only the viewer's
estimated slice (amount / p_count) rather than crediting them with the
full multi-recipient donation. `CampaignDetailPage` keeps its
group-by-(txid, donor) reply rendering so legacy multi-event donations
still collapse to a single donation card.
2026-05-18 16:36:32 -05:00
lemon 634e161085 Remove ephemeral geo chat 2026-05-18 09:49:31 -07:00
lemon f665ffa0c0 Flatten campaign form details 2026-05-17 23:12:13 -07:00
lemon ba2c541c31 Add country tags to campaigns 2026-05-17 23:12:13 -07:00
mkfain 323c613222 Add archive flow for campaigns
Authors can soft-close a campaign by republishing it with a
`["status", "archived"]` tag. Archived campaigns are hidden from the
main fundraisers feed and the donate button is disabled, but the detail
page still loads by direct link so existing donors can find it and
past donations remain attached. The author sees Archive / Reopen
buttons on the detail page and an Archived badge on cards.

useCampaigns gains an `includeArchived` option (default false) so a
future profile view can opt in. NIP.md documents the new status tag.
2026-05-17 20:20:44 -05:00
Alex Gleason 1db8b4d5b0 Add fundraising campaigns as the new home surface
Pivot the homepage from a Twitter-style social feed to a GoFundMe-style
fundraising hub. Introduces a new addressable kind 30223 "Campaign" that
carries the marketing-style metadata (title, summary, cover image, story,
category, goal, deadline, location) plus a list of recipient pubkeys with
optional split weights. Documented in NIP.md alongside the kind 8333
onchain-zap spec it builds on.

Donations are sent as a single multi-output Bitcoin transaction (one
output per recipient, derived Taproot addresses) using the existing
buildUnsignedMultiOutputPsbt + useBitcoinSigner infrastructure that
backs community on-chain zaps. After broadcast, the client publishes
one kind 8333 receipt per recipient with the campaign's `a` coordinate
so the donation aggregates into the campaign's totals.

UI surfaces:

- /campaigns is now the default homePage. Hero, two featured slots
  (placeholders in src/lib/featuredCampaigns.ts), then a grid of
  user-submitted campaigns.
- /campaigns/new is a full create form with cover upload, slug
  collision check, recipient builder with per-row weights, and
  preset/custom donation-amount UX.
- naddr1 identifiers for kind 30223 route to CampaignDetailPage via
  NIP19Page (full story rendered through the existing ArticleContent
  markdown component, plus a sticky donate rail with progress).
- DonateDialog presets are tuned for on-chain amounts (10K-1M sats)
  with a dust-aware minimum guard derived from the split math.
- Fundraisers sidebar item with a HandHeart icon.

Kept the existing social-feed pages addressable from the sidebar; the
overhaul is scoped to the home/landing experience rather than removing
the underlying Nostr features.
2026-05-17 16:48:06 -05:00
Alex Gleason c5b929187a Remove Nostr direct messaging feature
The @samthomson/nostr-messaging library opens fresh NRelay1 sockets per
participant per relay outside the shared NPool, fanning out to every
conversation partner's NIP-65 + NIP-17 inbox relays plus all
discoveryRelays in hybrid mode. In practice this drives connection counts
to several hundred relays per session.

Rather than band-aid the fan-out, drop the feature entirely and point
users to White Noise for end-to-end encrypted Nostr chat.

- Replace /messages with a 'Install White Noise' CTA card (route kept)
- Delete MessagingSettingsPage, DMProviderWrapper, messaging-intro.png
- Remove DMProvider wrapper and PROTOCOL_MODE config from App.tsx
- Drop messaging config from AppConfig, AppConfigSchema,
  EncryptedSettingsSchema, EncryptedSettings, and the NostrSync /
  useInitialSync sync paths
- Remove messages sidebar entry, default sidebarOrder slot, and
  SettingsPage messaging card
- Uninstall @samthomson/nostr-messaging and drop its tailwind content
  glob and vitest deps.inline entry
- Update copy in PrivacyPolicy, AdvancedSettings delete-account warning,
  ProfileSettings nsec warning, RequestToVanishDialog deletion checklist,
  MainLayout comment, and NIP.md
- Leave kind 4 rendering (EncryptedMessageContent) intact so DM events
  authored elsewhere still display in feeds and quote embeds
2026-05-17 14:37:49 -05:00
lemon ac48231e82 Document batch on-chain zaps 2026-05-17 01:12:16 -07:00
lemon 6d4d8ee9fb Document community actions 2026-05-16 09:03:45 -07:00
Alex Gleason 740fc1c63c Merge ditto/main into agora
Pulls in 387 commits from ditto/main while preserving Agora-specific
features. Where the two codebases diverged on the same concept, kept
the Agora side per project direction.

Kept Agora-specific:
- SparkWallet stack (over Ditto's nostr-derived Bitcoin wallet)
- Communities (NIP-72 + chat + members), Messages, Organizers,
  Actions, Verified, Appearance settings
- DMProviderWrapper, country/organizer moderation in NoteMoreMenu
- 'Agora' branding, pub.agora.app bundle ID, version 2.8.0
- Built-in theme system (src/themes.ts) only

Rejected from Ditto:
- All Blobbi virtual pet code (80+ files, route, provider, sidebar,
  kind labels, feed setting, NIP.md entries, CSS animations)
- Custom theme events (kinds 36767/16767) — ThemesPage, ThemeContent,
  active profile themes, theme snapshot recovery
- On-chain zaps (kind 8333) and the entire Bitcoin wallet implementation
  (useBitcoinWallet, bitcoin-signers, BitcoinContentHeader,
  bitcoinjs-lib / @bitcoinerlab/secp256k1 / ecpair / tiny-secp256k1)
- ZapSuccessScreen (depended on dropped bitcoin lib)

Pulled in from Ditto:
- .agents/skills/* (12 new specialized skills, slim AGENTS.md)
- @nostrify bumps to 0.52 / 0.6 / 0.37
- New routes/pages: Music, Podcasts, Videos, Vines, Wikipedia, Books,
  Bluesky, Archive, AIChat, Trends, Webxdc, Highlights, Decks, Emojis,
  Development, Treasures, Colors, Packs
- Birdstar feed integration (kinds 2473, 12473, 30621)
- Wikipedia/Wikidata/Scryfall lookup in ExternalContentPage
- release-notes CI job + extract-release-notes.mjs script
- nsite:// URI handling in feed/sidebar
- iOS fastlane setup
- src/lib/avatarShape.ts + Avatar shape prop (kept for new Music/People
  components that depend on it)

Preserved Agora's ABSOLUTE 'NEVER COMMIT' rule at the top of AGENTS.md
and dropped Ditto's contradicting 'Commit at the end of every task'
section.

Validation: npm run test passes (tsc, eslint, 40/40 vitest, vite build).
2026-05-13 18:35:03 -05:00
lemon 53e7122302 Add community chat tab 2026-05-11 11:12:13 -07:00
lemon ea4295cb89 Tighten flat community primitives
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.
2026-05-11 11:12:13 -07:00
lemon 5e99ac817b Document flat community membership 2026-05-11 11:12:13 -07:00
lemon a9ea21e3d4 Show bookmarked communities in My Communities via NIP-51 kind 10004
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.
2026-05-11 11:12:13 -07:00
filemon 478f53177e Bound social care history and simplify Blobbi social consolidation
- Gate social actions by projected stat thresholds (< 70) so visitors
      can only help stats in visual distress
    - Add energy category with Energy Drink and Power Nap Pillow items
    - Apply 6-hour recency window to interaction queries (limit 30)
    - Fix BlobbiActionsProvider tree placement so BlobbiPage shares context
      with the companion layer
    - Preserve event content in dev editor (don't overwrite checkpoint JSON)
    - Show Needs Now summary in activity tab with priority badges
    - Remove unused need-driven consolidation infrastructure

   Regression-of: 9aecefff
2026-05-06 22:27:50 -03:00
filemon 20d7aa199d Merge branch 'main' into feat/blobbi-1124-interactions 2026-05-04 13:29:31 -03: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
Alex Gleason b2634d2fcb Render Birdstar Birdex events (kind 12473) as tiled life lists
A Birdex is a replaceable per-author index of every bird species the
author has ever confirmed via kind 2473, stored as positional i/n tag
pairs in chronological first-seen order. In feeds, show a compact
tile strip of the most recently-added species with a "+N" capstone
when the list overflows — mirroring how kind 3 follow lists preview
members as an avatar stack plus "+N more". On the post-detail page,
render every species as a responsive grid so visitors can browse the
author's whole life list.

Each tile resolves the species' Wikidata entity through English
Wikipedia to pull a thumbnail and common-name label, reusing the
same fetch path as kind 2473 detection cards. The Wikidata URL is
sanitized before being routed, and the paired n tag provides a
scientific-name fallback while the remote lookup is in flight.
2026-04-30 01:44:15 -05:00
sam b46703eaed remove blobbis 2026-04-30 13:19:22 +07:00
filemon 9483fbc99a Merge branch 'main' into feat/blobbi-1124-interactions 2026-04-30 00:56:01 -03:00
Alex Gleason c5d5165f84 Merge remote-tracking branch 'origin/main' into wallet
# Conflicts:
#	package-lock.json
#	src/components/CommentContext.tsx
#	src/components/ExternalContentHeader.tsx
#	src/components/NoteCard.tsx
#	src/pages/VinesFeedPage.tsx
2026-04-29 21:44:07 -05:00
filemon 222f641123 Merge branch 'main' into feat/blobbi-1124-interactions 2026-04-29 14:58:01 -03:00
sam 3f28bf571a remove all theme stuff 2026-04-29 22:04:39 +07:00
Alex Gleason c957041cf3 Render Birdstar bird detections and custom constellations
Adds feed support for kind 2473 (bird-by-ear detections) and kind 30621
(user-drawn star figures) from Birdstar. Detections render as species
cards using the existing Wikidata + Wikipedia summary hooks; constellations
render as gnomonically-projected SVG star-maps backed by the Hipparcos
catalog from d3-celestial. The 1.1 MB catalog is code-split via lazy() so
it only loads when a constellation event is actually viewed.
2026-04-28 03:12:24 -05:00
filemon 6684efd146 Add Blobbi social interactions (kind 1124)
Introduce external social interactions for Blobbi companions. Other users
can feed, play, clean, and medicate a Blobbi they don't own via kind 1124
events. The owner's canonical 31124 state absorbs these interactions
through a checkpoint-based consolidation system with auto-sync on page load.

Key additions:
- Kind 1124 event schema with validation, parsing, and deterministic sort
- Social projection pipeline (read-only stat overlay from pending interactions)
- Owner-side consolidation into canonical state with checkpoint advancement
- Auto-sync hook (useCanonicalSync) triggered when owner opens /blobbi
- Social permission toggle (open/closed tag on 31124)
- Interaction UI: popover with item carousel on feed cards, detail pages,
  and the owner dashboard action bar
- Interaction reactions: facial expressions, sparkles, bubbles, floating
  hearts with phase-based animation system
- Activity tab showing interaction history with caretaker attribution
- BlobbiSocialActions component with egg gating and cooldown logic
- NIP.md documentation for the new kind
2026-04-27 23:02:19 -03:00
lemon c91bdc1d89 Harden community moderation foundation 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
sam 89a3562a1e update nips used doc 2026-04-27 18:33:33 +07:00
Alex Gleason b5b7424472 Harden Bitcoin zap implementation from code review
- Add 13 regression tests for Taproot address derivation, pubkey
  validation, npub→address, and mainnet address validation
- Validate pubkey hex format (/^[0-9a-fA-F]{64}$/) in
  nostrPubkeyToBitcoinAddress to fail fast on malformed input
- Match tapInternalKey against the signer's x-only pubkey in
  signPsbtLocal, per the BITCOIN-SIGNING.md spec ("inputs whose
  tapInternalKey does not match the signer's key MUST be left
  unchanged"). Throw if no owned inputs are found.
- Use >= DUST_LIMIT (not >) for change-output dust check, so a change
  of exactly 546 sats is preserved rather than donated to fees
- Extract formatBTC() helper into lib/bitcoin.ts; remove duplicated
  replace(/\.?0+$/, '') from WalletPage, SendBitcoinDialog, and
  BitcoinContentHeader
- Register kind 8333 ("Bitcoin zap") in CommentContext KIND_LABELS,
  CommentContext KIND_ICONS, NoteCard KIND_HEADER_MAP,
  signerWithNudge KIND_LABELS, and shellTitleForKind
- Disambiguate sign_psbt errors in NConnectSignerBtc: only re-wrap as
  "doesn't support sending Bitcoin" when the error message looks like
  a capability failure (unknown method, not implemented, etc.);
  propagate transient errors unchanged
- Show the recipient's derived Bitcoin address in OnchainZapContent
  so users can verify the destination before signing
- Clear knownUnsupportedBunkers on logout so a fresh login with an
  upgraded bunker isn't tainted by a previous session's rejection
- Reject self-zaps in verifyOnchainZap (sender == recipient)
- Update NIP.md to specify: change-output handling, amount-cap vs
  discard semantics, self-zap rejection, mempool/confirmation policy,
  and mainnet-only scope
- Delete unused useNsecAccess hook
2026-04-22 16:39:21 -05:00
Alex Gleason 0d637a55b1 Rename onchain zap kind 3043 → 8333
8333 is the Bitcoin mainnet P2P port, creating a clean semantic parallel
with NIP-57: kind 9735 (Lightning's P2P port) for Lightning zaps, kind
8333 for on-chain zaps. 3043 was the first free kind the generator
returned and carried no meaning.
2026-04-22 15:23:17 -05:00
Alex Gleason bddfe4b838 Add on-chain Bitcoin zaps as the default zap method
Introduce kind 3043, a new Nostr event that attests an on-chain Bitcoin
payment against a target event or profile. Because every Nostr pubkey
deterministically maps to a Taproot address, any user can receive an
on-chain zap without configuring lud06/lud16 — the zap button now
appears on every post whose author is not the current user.

Publishing flow: sender builds and broadcasts a Bitcoin transaction
paying the recipient's derived Taproot address, then publishes a
kind 3043 event with an `i` tag (`bitcoin:tx:<txid>`), the recipient's
`p`, the target's `e` / `a`, and a self-reported `amount` in sats.
Before displaying or counting a kind 3043 event clients verify the
referenced transaction on-chain and use the sum of outputs paying the
recipient's address as the authoritative amount, capping the sender's
claim at the verified value to prevent spoofing.

Lightning zaps remain available as an opt-in tab inside the zap dialog
whenever the author has a Lightning address configured; otherwise the
dialog is purely on-chain. Defaults favour on-chain: USD amount presets
($1 / $5 / $10 / $25 / $100), fee-speed selection, and a 3-step
form → confirm → success flow mirroring SendBitcoinDialog.
2026-04-22 15:17:09 -05:00
lemon e1d4939c81 add hierarchical communities protocol spec to NIP.md 2026-04-19 17:55:20 -07:00
sam 6d51f6eeac geo chat wip 2026-04-18 19:12:13 +05:45
sam 55fe82adf9 community stats 2026-04-18 16:28:41 +05:45
sam ced5d00163 ported actions/challenges 2026-04-18 13:38:50 +05:45
Mary Kate Fain 72a25d09aa feat: show tracks on playlist detail page with full playback and album support
- Add usePlaylistTracks hook to resolve a-tag refs into ordered track events
- Render track list in PlaylistDetail with Play All button and per-track
  playlist playback via playPlaylist()
- Support albums as playlists tagged with t:album, showing release date,
  label, and Disc3 icon
- Add All/Playlists/Albums filter toggle to MusicPlaylistsTab
- Show Album badge on playlist cards in grid views
- Add KIND_HEADER_MAP entries for music tracks (36787) and playlists (34139)
- Fix shellTitleForKind to return 'Playlist Details' for kind 34139
- Document music kinds and album convention in NIP.md
2026-04-17 15:25:06 -05:00
Chad Curtis a27ee3af86 Fix self-review findings: remove dead code, fix task progress display, fix hydration race
- Remove dead code: useSyncTaskCompletions, incrementInteractionTaskTags,
  getInteractionCount, getEvolveInteractionCount, unused lookup maps
- Fix task progress showing 0/N on load: compute event-based task counts
  directly from Nostr query results (authoritative) instead of relying
  solely on the evolution mission store which may not be hydrated yet.
  Use max(queryCount, missionCount) so progress displays immediately.
- Fix hydration race: useDailyMissions raw memo now waits for hydration
  before creating fresh missions, preventing overwrite of persisted
  evolution[] with empty array. Also preserve evolution missions across
  daily resets during hydration.
- Fix session store miss: use ensureSessionStore in incubation/evolution
  start so evolution missions are always populated even if the store
  hasn't been hydrated yet.
- Extract duplicate findMission to shared findEvolutionMission in
  evolution-missions.ts
- Document evolution[] field on kind 11125 in NIP.md
2026-04-16 16:17:57 -05:00
Alex Gleason c4354774ad Add Color Moments and Geocaching to community NIP specs, sort table by kind number 2026-04-01 18:05:52 -05:00