The README's "Wiring relays into the store" tutorial used a
per-subscription SubscriptionListener.onEvent callback to feed the
store. EventCollector landed exactly to remove that boilerplate, so
update the example to register one connection-level collector at app
init and stop wiring listeners per subscribe() call.
The "Building a reactive feed UI" example showed project() ->
stateIn(WhileSubscribed) but never opened the relay subscription.
Bind it to the projection's collection lifecycle with .onStart {
client.subscribe } / .onCompletion { client.unsubscribe }, so the
relay subscription's lifetime equals the UI collector's. Also
demonstrate ProjectionState.filterItems for narrowing without a
re-query.
Drop the redundant client.connect() call from the wiring example
and add an explicit note (in both README and CLIENT) that
NostrClient connects on demand from subscribe()/publish() — connect()
is only useful after disconnect().
Other small fixes:
- client.send(...) -> client.publish(...) (the actual API name).
- Rename the variable from `observable` to `db` to match the demoApp
code and the conceptual "this is the local database" framing.
- Wrap the publish path in a viewModelScope.launch so the snippet
is copy-pasteable into a ViewModel.
Tutorial-style README covering the full pipeline:
relay → NostrClient → ObservableEventStore → InterningEventStore →
EventStore → project() → ViewModel → Compose.
Two worked examples:
1. Wiring NostrClient subscriptions into the store via
SubscriptionListener.onEvent.
2. Building a reactive feed UI with project().stateIn(...) and
Compose, showing how the three reactivity layers (Loading/Loaded
state, list membership, per-event handles) map to Compose's
recomposition model.
Cross-links the existing CLIENT.md, RELAY.md, and store/sqlite
README. Pointers to per-class KDoc for projection internals.
https://claude.ai/code/session_01Jny85MTu1ynKgFBgysfWu5