diff --git a/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala b/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala index 88bc5a0..2e7479c 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala @@ -1,6 +1,7 @@ package works.iterative package ui.components +import zio.* import works.iterative.core.MessageCatalogue import works.iterative.core.auth.UserProfile import com.raquo.airstream.core.Signal @@ -18,6 +19,7 @@ def messages: MessageCatalogue def modal: Modal def dispatcher: ZIODispatcher[Env] + def runtime: Runtime[Env] def nested(prefixes: String*): ComponentContext[Env] = ComponentContext.Nested[Env](this, prefixes) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala b/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala index 88bc5a0..2e7479c 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala @@ -1,6 +1,7 @@ package works.iterative package ui.components +import zio.* import works.iterative.core.MessageCatalogue import works.iterative.core.auth.UserProfile import com.raquo.airstream.core.Signal @@ -18,6 +19,7 @@ def messages: MessageCatalogue def modal: Modal def dispatcher: ZIODispatcher[Env] + def runtime: Runtime[Env] def nested(prefixes: String*): ComponentContext[Env] = ComponentContext.Nested[Env](this, prefixes) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarExtensions.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarExtensions.scala index 62b2ea0..ab469f2 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarExtensions.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarExtensions.scala @@ -5,6 +5,7 @@ import works.iterative.core.{MessageId, UserMessage} import works.iterative.ui.components.ComponentContext import zio.IsSubtypeOfError +import works.iterative.ui.model.Computable object LaminarExtensions extends I18NExtensions with ZIOInteropExtensions @@ -40,6 +41,14 @@ import zio.{Fiber, Runtime, Unsafe, ZIO} extension [R, E, O](effect: ZIO[R, E, O]) + def computableUpdate(using + ev: E IsSubtypeOfError UserMessage + ): ZIO[R, Nothing, Computable.Update[O]] = + effect + .map(Computable.Update.Done(_)) + .mapError(msg => Computable.Update.Failed(ev(msg))) + .merge + def toEventStream(using runtime: Runtime[R])(using ev: E IsSubtypeOfError Throwable ): EventStream[O] = diff --git a/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala b/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala index 88bc5a0..2e7479c 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala @@ -1,6 +1,7 @@ package works.iterative package ui.components +import zio.* import works.iterative.core.MessageCatalogue import works.iterative.core.auth.UserProfile import com.raquo.airstream.core.Signal @@ -18,6 +19,7 @@ def messages: MessageCatalogue def modal: Modal def dispatcher: ZIODispatcher[Env] + def runtime: Runtime[Env] def nested(prefixes: String*): ComponentContext[Env] = ComponentContext.Nested[Env](this, prefixes) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarExtensions.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarExtensions.scala index 62b2ea0..ab469f2 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarExtensions.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarExtensions.scala @@ -5,6 +5,7 @@ import works.iterative.core.{MessageId, UserMessage} import works.iterative.ui.components.ComponentContext import zio.IsSubtypeOfError +import works.iterative.ui.model.Computable object LaminarExtensions extends I18NExtensions with ZIOInteropExtensions @@ -40,6 +41,14 @@ import zio.{Fiber, Runtime, Unsafe, ZIO} extension [R, E, O](effect: ZIO[R, E, O]) + def computableUpdate(using + ev: E IsSubtypeOfError UserMessage + ): ZIO[R, Nothing, Computable.Update[O]] = + effect + .map(Computable.Update.Done(_)) + .mapError(msg => Computable.Update.Failed(ev(msg))) + .merge + def toEventStream(using runtime: Runtime[R])(using ev: E IsSubtypeOfError Throwable ): EventStream[O] = diff --git a/ui/js/src/main/scala/works/iterative/ui/scenarios/ScenarioMain.scala b/ui/js/src/main/scala/works/iterative/ui/scenarios/ScenarioMain.scala index 0bac10d..b0f9065 100644 --- a/ui/js/src/main/scala/works/iterative/ui/scenarios/ScenarioMain.scala +++ b/ui/js/src/main/scala/works/iterative/ui/scenarios/ScenarioMain.scala @@ -1,5 +1,6 @@ package works.iterative.ui.scenarios +import zio.* import com.raquo.laminar.api.L.* import com.raquo.waypoint.* import org.scalajs.dom @@ -48,13 +49,14 @@ ) def main(@unused args: Array[String]): Unit = - given ComponentContext[Nothing] with + given ComponentContext[Any] with val currentUser: Signal[Option[UserProfile]] = Val(None) val messages: MessageCatalogue = messageCatalogue val modal: Modal = new Modal: override def open(content: HtmlElement): Unit = () override def close(): Unit = () - val dispatcher: ZIODispatcher[Nothing] = ZIODispatcher.empty + val dispatcher: ZIODispatcher[Any] = ZIODispatcher.empty + val runtime: Runtime[Any] = Runtime.default def container: HtmlElement = div( diff --git a/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala b/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala index 88bc5a0..2e7479c 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala @@ -1,6 +1,7 @@ package works.iterative package ui.components +import zio.* import works.iterative.core.MessageCatalogue import works.iterative.core.auth.UserProfile import com.raquo.airstream.core.Signal @@ -18,6 +19,7 @@ def messages: MessageCatalogue def modal: Modal def dispatcher: ZIODispatcher[Env] + def runtime: Runtime[Env] def nested(prefixes: String*): ComponentContext[Env] = ComponentContext.Nested[Env](this, prefixes) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarExtensions.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarExtensions.scala index 62b2ea0..ab469f2 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarExtensions.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarExtensions.scala @@ -5,6 +5,7 @@ import works.iterative.core.{MessageId, UserMessage} import works.iterative.ui.components.ComponentContext import zio.IsSubtypeOfError +import works.iterative.ui.model.Computable object LaminarExtensions extends I18NExtensions with ZIOInteropExtensions @@ -40,6 +41,14 @@ import zio.{Fiber, Runtime, Unsafe, ZIO} extension [R, E, O](effect: ZIO[R, E, O]) + def computableUpdate(using + ev: E IsSubtypeOfError UserMessage + ): ZIO[R, Nothing, Computable.Update[O]] = + effect + .map(Computable.Update.Done(_)) + .mapError(msg => Computable.Update.Failed(ev(msg))) + .merge + def toEventStream(using runtime: Runtime[R])(using ev: E IsSubtypeOfError Throwable ): EventStream[O] = diff --git a/ui/js/src/main/scala/works/iterative/ui/scenarios/ScenarioMain.scala b/ui/js/src/main/scala/works/iterative/ui/scenarios/ScenarioMain.scala index 0bac10d..b0f9065 100644 --- a/ui/js/src/main/scala/works/iterative/ui/scenarios/ScenarioMain.scala +++ b/ui/js/src/main/scala/works/iterative/ui/scenarios/ScenarioMain.scala @@ -1,5 +1,6 @@ package works.iterative.ui.scenarios +import zio.* import com.raquo.laminar.api.L.* import com.raquo.waypoint.* import org.scalajs.dom @@ -48,13 +49,14 @@ ) def main(@unused args: Array[String]): Unit = - given ComponentContext[Nothing] with + given ComponentContext[Any] with val currentUser: Signal[Option[UserProfile]] = Val(None) val messages: MessageCatalogue = messageCatalogue val modal: Modal = new Modal: override def open(content: HtmlElement): Unit = () override def close(): Unit = () - val dispatcher: ZIODispatcher[Nothing] = ZIODispatcher.empty + val dispatcher: ZIODispatcher[Any] = ZIODispatcher.empty + val runtime: Runtime[Any] = Runtime.default def container: HtmlElement = div( diff --git a/ui/shared/src/main/scala/works/iterative/ui/model/Computable.scala b/ui/shared/src/main/scala/works/iterative/ui/model/Computable.scala index e196a62..1c88f98 100644 --- a/ui/shared/src/main/scala/works/iterative/ui/model/Computable.scala +++ b/ui/shared/src/main/scala/works/iterative/ui/model/Computable.scala @@ -40,6 +40,18 @@ case _ => false object Computable: + enum Update[+A]: + case Loading extends Update[Nothing] + case Done(value: A) extends Update[A] + case Failed(msg: UserMessage) extends Update[Nothing] + + object Update: + extension [A](u: Update[A]) + def apply(c: Computable[A]): Computable[A] = u match + case Update.Loading => c.started + case Update.Done(v) => c.update(v) + case Update.Failed(msg) => c.fail(msg) + /** The initial state of a computable model */ case object Uninitialized extends Computable[Nothing]: