Commit Graph

3409 Commits

Author SHA1 Message Date
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 07ea1f94d1 Render full zap goal card on detail page and add FAB to community tabs
- PostDetailPage: render GoalContent for kind 9041 instead of plain text
- CommunityDetailPage: add floating action button on comments (compose) and fundraising (new goal) tabs, remove inline New Goal button
- CreateGoalDialog: support controlled open/onOpenChange props for external triggers
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 Thomson 2788127894 Merge branch 'ui/gut-blobbi' into 'main'
ui/gut-blobbi

See merge request soapbox-pub/agora-3!14
2026-04-30 07:11:33 +00:00
sam 9f425366c0 Merge branch 'main' into ui/gut-blobbi 2026-04-30 13:53:14 +07:00
Sam Thomson 0436949797 Merge branch 'refactor/changelog' into 'main'
refactor/changelog

See merge request soapbox-pub/agora-3!13
2026-04-30 06:49:31 +00:00
sam 8fdb5cf1ad reset changelog to agora 2026-04-30 13:38:02 +07:00
sam b46703eaed remove blobbis 2026-04-30 13:19:22 +07:00
Sam Thomson ecbee21d34 Merge branch 'ui/gut-custom-themes' into 'main'
ui/gut-custom-themes

See merge request soapbox-pub/agora-3!11
2026-04-29 15:08:41 +00:00
sam 3f28bf571a remove all theme stuff 2026-04-29 22:04:39 +07:00
sam 2e7eee66ee gut theme customisation 2026-04-29 21:47:36 +07:00
Sam Thomson 7c4d3012ec Merge branch 'fix/discovery-relay-selection' into 'main'
fix/discovery-relay-selection

See merge request soapbox-pub/agora-3!10
2026-04-29 13:47:31 +00:00
sam 01af784953 fix discovery relay selection 2026-04-29 20:41:41 +07:00
Sam Thomson da8a5e1dde Merge branch 'refactor/tidy-some-pages' into 'main'
refactor/tidy-some-pages

See merge request soapbox-pub/agora-3!9
2026-04-28 12:48:42 +00:00
sam e3b16a3c5b Merge branch 'main' into refactor/tidy-some-pages 2026-04-28 19:48:08 +07:00
sam a5849fc747 copy updates 2026-04-28 19:41:58 +07:00
sam 42430e510d update messaging dep so that syncing thing stays visible 2026-04-28 19:24:31 +07:00
sam 09c364b060 hide the messaging header 2026-04-28 19:24:19 +07:00
sam d96361c578 updated messaging dep 2026-04-28 18:39:32 +07:00
sam 1346112f36 feed title for consistency 2026-04-28 16:03:31 +07:00
sam 44b1019d98 header to notifs page 2026-04-28 15:51:08 +07:00
sam b4c5db0c0e dont wash out inactive cards so much, let errors be conveyed. and fixed layout issues with action sponsor overflows 2026-04-28 15:47:21 +07:00
sam a56b4839c8 let images fail fast and fix double header 2026-04-28 14:47:06 +07:00
sam 5768dc9183 llm -> fail fast, don't swallow errors 2026-04-28 14:46:20 +07:00
sam e871229248 update verified follow pack to new layout 2026-04-28 14:21:17 +07:00
sam 141166cdc8 copy 2026-04-28 14:20:03 +07:00
Sam Thomson b99590bc5e Merge branch 'feat/community-reporting' into 'main'
Community Moderation

See merge request soapbox-pub/agora-3!7
2026-04-28 04:10:13 +00:00
lemon b2f4cc3583 Make members-only toggle reactive and move it off the tab row
Two fixes on the members-only filter UI:

1. Toggling the shield now updates feeds live, without a reload.

The previous implementation used `useLocalStorage` in two separate
components. Each call instantiates its own `useState`, so writes from
one didn't flow to the other's reader. `localStorage`'s `storage`
event only fires cross-tab, not in the tab that wrote — so same-tab
consumers stayed stale until a remount.

Replaced with a module-level singleton store subscribed via
`useSyncExternalStore`. All consumers share one source of truth;
toggling rerenders every subscriber in the same tab instantly. The
store still persists to localStorage and listens for cross-tab
`storage` events, so behaviour across tabs is unchanged.

2. Move the shield off the CommunityDetailPage tab row.

Placing the toggle inline with the TabsList made it sit on the
bottom-border stroke that belongs to the tabs, reading as if the
shield itself were an underlined tab. Moved it up one row, right-
justified on the "Founded by" label row. Visually cleaner and still
scopes the filter to the entire community (all content feeds under
the tabs, current and future), not any single tab.
2026-04-27 09:22:08 -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 8923aa87e2 Remove unfinished community events tab
Drops the read-only calendar-events (kind 31922/31923) listing from
CommunityDetailPage. The feature was partial — events could be listed
but not created from the community context — and the moderation /
authorship model for community-scoped events needs its own design
pass. Keeping it half-shipped complicates the moderation foundation
this branch is establishing.

A proper community events implementation will land in its own MR with
clearer scope: creation, RSVP handling, moderation rules for
community-scoped NIP-52 events, and whether the activity feed should
surface them.

General (non-community) calendar event support is unaffected —
EventsFeedPage, CalendarEventContent, CalendarEventDetailPage, RSVP
hooks, and the feed dispatch all remain. The community activity feed
already did not include kind 31922/31923, so no change there.
2026-04-27 09:22:08 -07:00
lemon 527b31247b Restore moderation trust boundary and remove unsafe cache seeding
Two fixes prompted by external review:

1. resolveCommunityModeration now takes the community A tag and filters
   events by matching `A` tag as its first pass. The previous change
   removed the A-tag existence check from parseCommunityReport on the
   assumption that callers scope by relay `#A` filter; that was an
   invariant of the current callers, not a property of the API. Moving
   the check to the resolver restores the trust boundary at the public
   API surface while keeping parseCommunityReport a pure single-event
   parser. The activity feed's pre-grouping pass is dropped since the
   resolver now handles per-community filtering itself.

2. Drop the `['community-members', aTag]` cache seeding from the
   activity feed. The activity feed uses shared relay limits across
   every subscribed community (500 awards and 500 reports total), so
   per-community results can be truncated. Seeding the per-community
   members cache with incomplete data would silently corrupt membership,
   authority, and moderation state on community detail pages.
   useCommunityMembers remains the authoritative per-community fetch.
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 7440b2d620 Improve community moderation structure and naming for reviewability
Rename memberMap -> rankMap to clarify it is a pre-moderation rank lookup
(includes banned members) and should not be used to list active members.

Extract canBanTarget(), getViewerAuthority(), isEventAllowedByModeration(),
CommunityMenuContext, and EMPTY_MODERATION into communityUtils as shared
primitives, eliminating duplicated logic across hooks and components.

Remove unused ApplyCommunityModerationOptions dead code.
2026-04-27 09:22:08 -07:00
lemon f48ba562ea Clarify community moderation UI labels 2026-04-27 09:22:08 -07:00
lemon c91bdc1d89 Harden community moderation foundation 2026-04-27 09:22:08 -07:00
lemon c7b3305ef4 Void bans and reports from banned members
Rework resolveCommunityModeration into a two-pass approach so that
members who are themselves banned cannot retain moderation authority:

Pass 1: collect valid ban candidates, sort by reporter rank ascending,
then apply them — skipping any candidate whose reporter was already
banned by a higher-ranked member earlier in the pass.

Pass 2: collect non-ban reports, skipping reporters who ended up in
the banned set from pass 1.
2026-04-27 09:22:08 -07:00
lemon 09c904917d Show moderation actions in three-dot menu across all community contexts
The NoteMoreMenu 'Remove post' and 'Ban' options were only visible on
the community detail page where CommunityModerationContext was provided.
Now they also appear in the activities feed and post detail page.

- Add useCommunityModeration hook for PostDetailPage (resolves community
  context from event's A tag with lazy queries)
- Extend useCommunityActivityFeed to expose per-community memberMap and
  moderation data (zero extra queries — reuses already-fetched data)
- Wrap each NoteCard in ActivitiesTab with CommunityModerationContext
- NoteCard itself is untouched — no performance impact on other feeds
2026-04-27 09:22:08 -07:00
lemon 3e099bb08d apply moderation filter to activites 2026-04-27 09:22:08 -07:00
lemon 0e99250a3b Unify more-menu sections and rename 'Remove content' to 'Remove post'
Merge the three middle sections into one, remove stale JSDoc referencing
deleted reinstatement logic, and align dialog/toast copy with menu label.
2026-04-27 09:22:08 -07:00
lemon 9be5650dcd Improve community moderation robustness and efficiency
- Eliminate double resolveMembership call by filtering banned members post-hoc
- Memoize community context derivation in NoteMoreMenu
- Hoist viewerMember lookup out of render loop in CommunityDetailPage
- Only mount BanConfirmDialog when viewer has ban authority
- Deduplicate NIP-56 report type definitions into canonical source
2026-04-27 09:22:08 -07:00
lemon 3efdcd5a63 Remove deletion/reinstatement logic from community moderation
Reinstatement via kind 5 deletions will be implemented in a future branch.
Removing it now eliminates an overly-broad unscoped query and a security
issue where any pubkey could reinstate banned content.
2026-04-27 09:22:07 -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 Thomson 0940358fba Merge branch 'feat/dms' into 'main'
feat/dms

See merge request soapbox-pub/agora-3!8
2026-04-27 12:07:17 +00:00
sam 50637a4dc1 updated to latest messaging package to resolve peer dep issue 2026-04-27 18:44:30 +07:00
sam 89a3562a1e update nips used doc 2026-04-27 18:33:33 +07:00
sam 2852590e09 updated messaging dep 2026-04-27 13:09:54 +07:00
sam b5c941f9fb use latest messaging dep 2026-04-25 20:34:46 +07:00
sam 9cdbb7c9e8 expose legacy nip4 option in settings 2026-04-25 19:05:51 +07:00
sam 0c9da915ef bring messaging settings over 2026-04-25 18:20:03 +07:00
sam 94ca6d162f Merge branch 'main' into feat/dms 2026-04-25 00:41:19 +07:00