Managed clock subscription
Start the clock at one tick per second, then replace it with a faster stream without managing a fiber yourself. Cancel stops delivery while retaining the visible tick history. Reset both cancels the stream and restores the initial model.
Each embedded instance derives its SubscriptionKeyopaque type SubscriptionKey = StringA nominal runtime key for one managed LiveView subscription.
from its documentation instance ID. The key names one resource inside its
owning LiveView; it is not application state or a global subscription name.
final class SubscriptionClockExample(instanceId: String)
extends LiveView[SubscriptionClockExample.Msg, SubscriptionClockExample.Model]:
import SubscriptionClockExample.*
private val ClockSubscription = subscriptionKey(instanceId)
def mount(ctx: MountContext): Task[Model] =
ZIO.succeed(Model())
def handleMessage(model: Model, ctx: MessageContext) =
case Msg.Start =>
if model.mode == Mode.Stopped then
ctx.subscriptions
.start(ClockSubscription, SubscriptionDelivery.Lossless)(ticks(1.second))
.as(model.copy(mode = Mode.EverySecond))
else ZIO.succeed(model)
case Msg.Replace =>
ctx.subscriptions
.replace(ClockSubscription, SubscriptionDelivery.Lossless)(ticks(250.millis))
.as(model.copy(mode = Mode.FourTimesPerSecond))
case Msg.Cancel =>
ctx.subscriptions.cancel(ClockSubscription).as(model.copy(mode = Mode.Stopped))
case Msg.Reset =>
ctx.subscriptions.cancel(ClockSubscription).as(Model())
case Msg.Tick(at) =>
ZIO.succeed(model.copy(lastTick = Some(at), tickCount = model.tickCount + 1))
override def view(model: Signal[Model]): HtmlElement[Msg] =
div(
cls := "docs-managed-work",
sectionTag(
cls := "docs-managed-work-state",
aria.label := "Clock subscription state",
p("Mode", strong(dataAttr("clock-mode") := "", model.map(_.mode.label))),
p("Ticks received", strong(dataAttr("clock-count") := "", model.map(_.tickCount.toString))),
p(
"Latest tick",
span(dataAttr("clock-tick") := "", model.map(_.lastTick.fold("Waiting")(_.toString)))
)
),
div(
cls := "docs-managed-work-controls",
button(
typ := "button",
disabled := model.map(_.mode != Mode.Stopped),
on.click(Msg.Start),
"Start every second"
),
button(typ := "button", on.click(Msg.Replace), "Replace with fast clock"),
button(
typ := "button",
disabled := model.map(_.mode == Mode.Stopped),
on.click(Msg.Cancel),
"Cancel clock"
)
)
)
end SubscriptionClockExample
object SubscriptionClockExample:
enum Mode(val label: String):
case Stopped extends Mode("Stopped")
case EverySecond extends Mode("Every second")
case FourTimesPerSecond extends Mode("Four times per second")
final case class Model(
mode: Mode = Mode.Stopped,
lastTick: Option[Instant] = None,
tickCount: Int = 0)
enum Msg:
case Start
case Replace
case Cancel
case Reset
case Tick(at: Instant)
private[docs] def subscriptionKey(instanceId: String): SubscriptionKey =
SubscriptionKey(s"subscription-clock-$instanceId")
private def ticks(every: Duration): ZStream[Any, Nothing, Msg] =
ZStream.tick(every).mapZIO(_ => Clock.instant).map(Msg.Tick(_))View source (documentation/site/src/scalive/docs/examples/SubscriptionClockExample.scala:11-92)
Result
ConnectedReconnectingRead-onlyModeStopped
Ticks received0
Latest tickWaiting
Disconnected. Controls resume after reconnection.
Related guidance: own long-lived streams with subscriptions.