6.4 KiB
6.4 KiB
Group creation flow (nostrord parity)
Plan to bring the group creation flow in line with nostrord (https://github.com/nostrord/nostrord), a NIP-29 client. Analysis of both implementations, then a phased plan.
Current state (coop)
- Dialog (
crates/workspace/src/dialogs/new_group.rs): three plain text inputs (Name, Group id, Relay). Slug is generated from the name when the id is blank. Metadata isGroupMetadata::default()with only the name set. Errors surface as toast notifications only. - Protocol (
crates/nip29/src/protocol.rs):GroupId::create_groupsends kind 9007 with the full metadata in one event. No confirmed-group-id handling and no OK-retry on metadata. - Registry (
GroupsRegistry::createincrates/nip29/src/lib.rs): ensures and subscribes the group, upserts the list entry, publishes kind 10009, and on publish failure forgets the group and emitsGroupsEvent::Error.
Reference (nostrord)
UI (CreateGroupModal.kt)
- Title "Create a Group" ("Create Channel" for subgroups).
- Avatar upload row ->
picture. - Name (required, placeholder
#example). - Group ID optional, hidden behind a "Set a custom ID" disclosure; filtered to
lowercase
a-z0-9-_; hint "Leave empty for a random ID. The relay may override your choice." - Relay dropdown: user's relays + current relay + suggested NIP-29 relays + "Custom relay..." revealing a URL field. Locked to the parent's relay for subgroups.
- Description, multi-line (3-5 rows) ->
about. - ACCESS SETTINGS: switch rows with NIP-29 copy — Private (only members can read), Closed (join requests ignored), Restricted (only members can post), Hidden (not discoverable).
- YOUR LIST: "Private on my list" — keeps the membership in the NIP-44 encrypted section of kind 10009.
- Errors: friendly mapping (
blocked:-> creation must happen on the relay's website,auth-required-> authorization required, timeouts, ...) plus an "Open relay website ->" link (wss -> https). - Submit: spinner while in flight, cancelable; validation (name required, valid relay URL).
Protocol (GroupManager.createGroup)
Two-step flow:
- Group ID: custom (trimmed, lowercased) or a random readable id
(
ReadableId.generate()). - Kind 9007 (create-group) with only the
htag -> await relay OK -> await the relay's kind 39000 to learn the confirmed group id (relays may override the suggested id). - Kind 9002 (edit-metadata) with
h+ name + access flags (presence-only tags) + about + picture + parent -> await OK (7s timeout, one retry after 1s to cover the admin-grant race). If still rejected, keep the group and warn that the relay did not accept its name. - Bookkeeping: persist membership, discard the self-invite race, set the private-list flag before publishing kind 10009, publish the list, request group messages and metadata.
Existing coop infrastructure to reuse
Group::publishalready awaits the relay OK and surfaces the rejection reason (crates/nip29/src/group.rs).GroupId::edit_metadata/Group::edit_metadatafor kind 9002 (crates/nip29/src/protocol.rs,group.rs).ui::Switch(label + description),PopupMenudropdown,TextareaState,Avatar(crates/ui).- Blossom upload via
state::uploadwithAppSettings::get_file_server. - NIP-65 relay-list extraction, as in
dialogs/relays.rs(GossipRelays::load). - Group panel open flow used by the Browse panel
(
crates/workspace/src/panels/browse.rsopen_panel).
Plan
Phase 1 — Protocol layer (crates/nip29)
- Adopt the two-step creation in
protocol.rs:GroupId::create_groupemits kind 9007 with only thehtag; metadata moves to the existingedit_metadata(kind 9002) builder. - Add
GroupCreateOptions(name, about, picture, private/closed/restricted/ hidden, optional custom id, optional parent) passed toGroupsRegistry::create. - Rework
GroupsRegistry::create:- ID: custom (trim/lowercase/validate) or a random readable id (small port
of nostrord's syllable-based
ReadableId). ensure()-> publish 9007 viaGroup::publish(relay OK) -> wait briefly (with timeout) for the relay's kind 39000 to confirm the group id (the group is already subscribed viaensure).- Publish 9002 with the full metadata; retry once after 1s; on persistent failure keep the group and emit a warning event (nostrord parity).
- On 9007 failure:
forget()+ emitGroupsEvent::Errorwith the relay's reason. - On success: upsert entry +
publish_list+ emit a newGroupsEvent::Created(GroupKey)so the UI can navigate.
- ID: custom (trim/lowercase/validate) or a random readable id (small port
of nostrord's syllable-based
Phase 2 — New dialog (crates/workspace/src/dialogs/new_group.rs)
Rebuild the modal to nostrord's layout and copy:
- Header + subtitle; name field; "Set a custom ID" disclosure with filtered input.
- Relay dropdown: user's NIP-65 relays (loaded like
GossipRelays::load) + suggested group relay + "Custom relay..." revealing a URL field with validation. - Description via
TextareaState(3-5 lines). - ACCESS SETTINGS: four
ui::Switchrows with nostrord's NIP-29 label/description copy. - In-flight state on the Create button (spinner, disabled), inline error row with the friendly error mapping + "Open relay website ->" link (wss -> https).
- Subscribe to
GroupsEvent::Created/GroupsEvent::Errorwhile open -> close the dialog and open the new group panel (same flow asbrowse.rs::open_panel).
Phase 3 — Avatar upload
- Picture row using
ui::Avatarpreview + file picker ->state::upload(existing blossom upload), wiring upload errors into the dialog's error row.
Phase 4 — Optional extensions
- "Private on my list": requires NIP-44 encrypted-section support in
GroupList(kind 10009 parse/build/load) — meaningful extra scope; candidate for a follow-up. - "Create Channel" subgroup mode: protocol support (parent tag) already exists; needs a trigger point in the group details/subgroups UI and relay-locking in the dialog.
Validation
cargo check/cargo clippyafter each phase.- Manual run of the dialog against a NIP-29 relay.
- Unit tests for the readable-id and input-filter helpers.
Open questions
- Include "Private on my list" now, or defer the encrypted kind 10009 work?
- Blank id behavior: nostrord generates a random readable id, coop currently slugs the name. Match nostrord?
- Avatar upload in scope for the first pass?
- Subgroup / "Create Channel" mode now, or group creation only?