Files
mantra-kmp/composeApp
Kgothatso Ngako b3379f8117 feat(groups): broadcast every accepted entry from one button, each to where its own would go
The entries screen had one "Broadcast" under each card, and each opened
`BroadcastGroupSignedEventScreen` for that one entry: a group that has accepted
twenty entries into its lists sent them with twenty trips through the relay
picker. This is the other button. "Broadcast all", an extended FAB in a bottom
bar, sends every entry on the screen, newest first, one after another, and says
under each card what its relays answered. Behind no gate, like the button under
each card: sending what the group has already signed takes no share of the key.

**Each entry goes where its own broadcast screen would have started.** The list
that screen seeds -- `defaultRelaysFor`: the group's General write relays or the
app's publish set, plus the list's own relays, minus what the group has blocked
-- is what a member who reads nothing and presses the button sends to, and this
button is that member with twenty entries. The group's relay lists are read
into the loaded state for it; the list's relays come off the entry, which
carries its schema, so the rule is handed that one schema rather than all of
the group's. Nothing is shown before the press, which is the difference from
the one-event screen and the reason that screen stays: a refusal is answered
there, where the relay can be changed and the send tried again.

**One entry at a time, on purpose.** The rows fill in top to bottom, so the
member can see where the batch is; and a public relay sent twenty events in
one burst answers "rate-limited" to most of them, which would make the button
worse than the presses it saves. Each entry's send has the same ceiling as a
single broadcast and the batch has no other, since a long list is meant to
take a while. It goes to the pool directly rather than through the queue, for
the reason `BroadcastGroupSignedEventViewModel` gives, and it is only as
durable as the press: leaving the screen ends it where it is.

**The send is shared rather than copied.** What a relay's answer becomes -- an
OK, a rejection in the relay's words, a failure, or no answer when the whole
send runs out of time -- was decided inside `broadcast()` on the one-event view
model. It is now `sendToRelays` on that model's companion, beside
`defaultRelaysFor` and `wireEventOf`, and `broadcast()` keeps only the state
around it; the thirteen tests on it pass unchanged. The words for an answer
moved the same way, out of the relay row and into `RelayOutcome.label()`, which
both screens now use.

**What the member is told.** A line above the cards -- "Sending entry 3 of
12…", then "Accepted by every relay for 11 of 12 entries." -- persistent rather
than a snackbar, because it is the answer the member came for and a snackbar
would be gone before the last card had been read. Under each card, "Accepted
by n of m relays." in the one-event screen's words, and one line per relay
that did not say OK, named with its reason, because "blocked: not on the allow
list" is something a member can act on and a count of two out of three is not.
An entry with nowhere to go -- everything the seed would name blocked -- is
marked so rather than sent to nobody. The FAB looks disabled while a batch is
out, and a second press is refused.

`GroupCuratedEntriesViewModelJvmTest` pins where each entry goes, that the next
waits for the last to settle, what the answers become, and the refusals; the
screen test drives the button end to end and checks that each answer sits
between its card and that card's own broadcast. The shared fixture entry now
carries the `a` root a real entry has -- without it the seed rule could not
find the list's relays, which the first run of these tests found.

Verified with :composeApp:m3Audit (all budgets met),
:composeApp:compileDebugKotlinAndroid and :composeApp:jvmTest (1085 tests,
7 new).

Replayed onto Mantra by docs/curated-to-mantra.md, in part: only its broadcast half: sendToRelays lifted into the view model's companion, the RelayOutcomeLabel widget and the screen reading it, and the GroupSignedEvent comment. The entries screen's Broadcast-all button, its view model and state, its tests, its five strings and its nav-host wiring are line D, which Mantra took out (docs/curated-to-mantra.md).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Pulled-From: curated/curated@d2a4298d25
2026-09-13 16:35:31 +02:00
..