A refresh policy for chain-derived data — the missing piece between
Block.watchBlockNumber and your RemoteCalls.
Chain data goes stale exactly once per block, so refetch cadence should be expressed in blocks, not milliseconds:
type alias Model =
{ ticker : BlockRefresh.Ticker
, balance : RemoteCall String
}
-- init
{ ticker = BlockRefresh.init (EveryNBlocks 5), ... }
-- on each { tag = "blockNumber" } port message:
let
( ticker, refetch ) =
BlockRefresh.onBlock number model.ticker
in
if refetch then
( { model | ticker = BlockRefresh.markRefreshed number ticker
, balance = RemoteCall.request "bal-..." model.balance }
, web3Cmd (Balance.encode ...)
)
else
( { model | ticker = ticker }, Cmd.none )That worked example IS the balance watcher: balances re-read on-cadence,
driven by chain time. Any RemoteCall slots in the same way.
Tracks the policy and the block height of the last refresh. Opaque.
Change cadence, keeping the last-refreshed watermark.
Feed a new block number in; the Bool says "fire your refetch now".
The first observed block always fires (except under Manual) — data that
has never been fetched is infinitely stale.
Record that the refetch was actually fired at this height. Separate from
onBlock so a refetch you decided to skip (e.g. one already in
flight) doesn't advance the watermark.
EveryBlock— refetch on every new block.EveryNBlocks n— refetch when at leastnblocks have passed since the last refresh.Manual— never fire from block signals (the app refetches explicitly).