A live on-chain event feed -- the binder between Web3.Subscription log
streams and rendered rows.
The port layer answers a subscription request with
{ tag = "subscribed", status = "open" | "failed" } -- failed meaning the
WebSocket was unavailable and the bridge fell back to polling, so events
still flow, just with more latency. The Feed tracks that honestly (a
Fallback chip, not a lie of liveness), keeps items newest-first, and
enforces a cap so a hot contract can't grow your model without bound.
-- MODEL
{ feed : EventFeed.Feed TokenCreated }
-- port `subscribed` for our correlation id:
{ model | feed = EventFeed.onSubscribed { open = ok } model.feed }
-- each decoded LogEvent:
{ model | feed = EventFeed.push tokenCreated model.feed }
-- VIEW
EventFeed.view
{ onLoadMore = Just LoadOlder } -- app fires a getLogs range query
viewTokenCreated
model.feedGeneric over the item type -- the app decodes its own events; this module never guesses at your ABI.
CSS classes: web3-eventfeed, web3-eventfeed__status,
web3-eventfeed__status--connecting/--live/--fallback/--closed,
web3-eventfeed__list, web3-eventfeed__more.
Where the stream stands. Fallback = events arrive by polling.
A feed awaiting its subscription confirmation. Caps below 1 are lifted to 1 -- a feed that can hold nothing is not a feed.
Apply the port's subscribed answer: open = True -> Live,
open = False -> Fallback (the bridge is polling; events still flow).
Render the feed: status chip, aria-live list, optional load-more.
The feed. Opaque -- the cap and ordering are invariants, not suggestions.