add NIP-29 moderation, pins, browse, and create-group support
This commit is contained in:
@@ -393,11 +393,50 @@ group's relay only, and treat the relay's `OK` as the truth.
|
||||
|
||||
1. Moderation writes: `9000` put-user (roles), `9001` remove-user, `9002`
|
||||
edit-metadata, `9005` delete-event, `9009` create-invite; gate the UI on the
|
||||
caller appearing in `39001`, let the relay be the enforcer.
|
||||
2. Group creation (`9007`) where the relay supports it, and relay browsing
|
||||
(`fetch_events(ReqTarget::single(relay, [Filter::new().kind(Kind::GroupMetadata).limit(..)]))`).
|
||||
3. Pins: `9010` write, `39005` read (`Kind::Custom`); subgroups: parse
|
||||
`parent`/`child` and lay groups out as a tree.
|
||||
caller appearing in `39001`, let the relay be the enforcer. **Done** as pure
|
||||
writes: builders on `GroupId`, senders on `Group` sharing `dispatch`.
|
||||
`edit_metadata` takes a `GroupMetadata`, which is now `Default`-able (its
|
||||
private `id`/`author` origin are `Option`) so the UI can start an edit from
|
||||
scratch or from `Group::metadata()`. **`9005` is write-only for now**: the
|
||||
relay stops serving the event but nothing is removed locally, so the deleted
|
||||
message lingers in the cache and timeline. The UI phase must drop the target
|
||||
envelope (filter on its `d` = target id and the group's `r` cache tag) and
|
||||
evict it from `messages`/`index`, otherwise only a reload clears it.
|
||||
2. Group creation (`9007`) where the relay supports it, and relay browsing.
|
||||
**Done.** `GroupId::create_group` reuses the `9002` metadata tags, so the
|
||||
proposed id (`h`) and the initial name/flags go in one event; the relay
|
||||
republishes `39000`, which the standing subscription picks up.
|
||||
`GroupsRegistry::create` mirrors `join`: `ensure` → optimistic `10009` entry
|
||||
+ `publish_list` → publish, and on `OK false` it `forget`s the group and emits
|
||||
`GroupsEvent::Error(reason)` (that variant was previously unused). There is
|
||||
no NIP-11 support check available, so "where the relay supports it" is
|
||||
enforced by the relay refusing the `9007`.
|
||||
`GroupsRegistry::browse(relay, cx)` follows the same pipeline as every
|
||||
other read: it registers a `brw-<hash>` subscription (an opaque id, like
|
||||
`grp-<hash>`; the relay is keyed in state, not encoded in the id, because the
|
||||
pump needs a prefix to recognise its own ids and a bare
|
||||
`SubscriptionId::generate()` would be indistinguishable from the other
|
||||
crates' ids on the shared client). `add_relay` +
|
||||
`subscribe` (auto-close on EOSE, 10s cap), and returns immediately. The
|
||||
**global pump** observes the events, and `handle_event`/`handle_eose`/
|
||||
`handle_closed` fold each relay-signed `39000` into `self.browse`, keyed by
|
||||
the subscription id, so no DB scoping is needed. `GroupsRegistry::browsed(relay)`
|
||||
returns `Browse { relay, loading, groups: Vec<GroupCandidate> }` for the UI, and
|
||||
`GroupsEvent::Updated` says when to re-read it. No `fetch_events` anywhere:
|
||||
the DB cannot be scoped to a relay (a stored event carries no origin), so the
|
||||
subscription's own events are the source. `join` now delegates to a new
|
||||
`join_key(key, invite)`, which is what a browse result needs (a candidate has
|
||||
a key, not a `GroupReference`).
|
||||
3. Pins and subgroups. **Pins done:** `Pin::{Event, Address}` and
|
||||
`GroupPins` in `protocol.rs`, with `PIN_LIST` (39005) and `UPDATE_PIN_LIST`
|
||||
(9010) as `u16` consts used through `Kind::Custom`. `39005` joins
|
||||
`STATE_KINDS`, so the standing subscription fetches it, `absorb_state` folds
|
||||
it into `Group::pins()`, and `Group::update_pins(pins)` writes `9010` through
|
||||
the shared `dispatch`. **Subgroups parsed:** `GroupMetadata` gained
|
||||
`pub children: Vec<GroupId>` (order preserved, parsed from `child` tags) and
|
||||
`metadata_tags` re-emits them — `9002` requires it, an edit that drops a
|
||||
child is rejected. `parent`/`children` are per-relay, so assembling the tree
|
||||
from `Group::metadata()` is UI work; there is no backend tree helper.
|
||||
4. Range backfill: page the timeline with `until` for "load more".
|
||||
5. LiveKit AV (`livekit` tag, `39004`, the `/.well-known/nip29/livekit` token
|
||||
endpoint with a NIP-98 `27235` authorization event) and fork/migration
|
||||
@@ -415,6 +454,11 @@ group's relay only, and treat the relay's `OK` as the truth.
|
||||
| Membership badge (member, pending, refused, removed) | `Group::membership()` |
|
||||
| Copy an invite link | `GroupMetadata::naddr(relay, invite)` |
|
||||
| Leave | `GroupsRegistry::leave` |
|
||||
| Create a group | `GroupMetadata { ..Default::default() }` → `GroupsRegistry::create(key, metadata, cx)`; failure surfaces as `GroupsEvent::Error` |
|
||||
| Browse a relay's groups | `GroupsRegistry::browse(relay, cx)` → re-read `GroupsRegistry::browsed(relay)` on `GroupsEvent::Updated`; join a result with `GroupsRegistry::join_key(candidate.key, None, cx)` |
|
||||
| Moderate (put/remove user, edit metadata, delete event, invite) | `Group::put_user/remove_user/edit_metadata/delete_event/create_invite` |
|
||||
| Pins | `Group::pins()` to read, `Group::update_pins(pins)` to replace the list |
|
||||
| Subgroup tree | `Group::metadata()` → `parent`/`children`, assembled per relay |
|
||||
|
||||
Where it lands: the sidebar gains a section reading `GroupsRegistry::groups()`
|
||||
(the same shape as its Communities section) with pending and refused groups
|
||||
|
||||
Reference in New Issue
Block a user