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 36fd8ea..df2cbf8 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 @@ -2,13 +2,25 @@ package ui.components import works.iterative.core.MessageCatalogue +import works.iterative.core.auth.UserInfo +import com.raquo.airstream.core.Signal -trait ComponentContext[App]: - def app: App +/** Context containing services needed in all parts of the application + */ +// TODO: this is Laminar-specific, we need to make it generic +// Also, it is hard to mock, as there is too much stuff in it +// There has been an attempt to use a typeclass to provide the services, like +// Env: CurrentUser etc, but it was unclear how the typeclass would be used with the Dispatcher +// as it needs Env +// So for now, it is everything in one place +trait ComponentContext[+Env]: + def currentUser: Signal[Option[UserInfo]] def messages: MessageCatalogue + def modal: Modal + def dispatcher: ZIODispatcher[Env] - def nested(prefixes: String*): ComponentContext[App] = - ComponentContext.Nested[App](this, prefixes) + def nested(prefixes: String*): ComponentContext[Env] = + ComponentContext.Nested[Env](this, prefixes) object ComponentContext: case class Nested[App](parent: ComponentContext[App], prefixes: Seq[String]) 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 36fd8ea..df2cbf8 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 @@ -2,13 +2,25 @@ package ui.components import works.iterative.core.MessageCatalogue +import works.iterative.core.auth.UserInfo +import com.raquo.airstream.core.Signal -trait ComponentContext[App]: - def app: App +/** Context containing services needed in all parts of the application + */ +// TODO: this is Laminar-specific, we need to make it generic +// Also, it is hard to mock, as there is too much stuff in it +// There has been an attempt to use a typeclass to provide the services, like +// Env: CurrentUser etc, but it was unclear how the typeclass would be used with the Dispatcher +// as it needs Env +// So for now, it is everything in one place +trait ComponentContext[+Env]: + def currentUser: Signal[Option[UserInfo]] def messages: MessageCatalogue + def modal: Modal + def dispatcher: ZIODispatcher[Env] - def nested(prefixes: String*): ComponentContext[App] = - ComponentContext.Nested[App](this, prefixes) + def nested(prefixes: String*): ComponentContext[Env] = + ComponentContext.Nested[Env](this, prefixes) object ComponentContext: case class Nested[App](parent: ComponentContext[App], prefixes: Seq[String]) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/Modal.scala b/ui/js/src/main/scala/works/iterative/ui/components/Modal.scala new file mode 100644 index 0000000..b9098da --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/Modal.scala @@ -0,0 +1,8 @@ +package works.iterative.ui.components + +import com.raquo.laminar.api.L.* + +trait Modal: + // TODO: this is wrong, we need to make it generic + def open(content: HtmlElement): Unit + def close(): Unit 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 36fd8ea..df2cbf8 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 @@ -2,13 +2,25 @@ package ui.components import works.iterative.core.MessageCatalogue +import works.iterative.core.auth.UserInfo +import com.raquo.airstream.core.Signal -trait ComponentContext[App]: - def app: App +/** Context containing services needed in all parts of the application + */ +// TODO: this is Laminar-specific, we need to make it generic +// Also, it is hard to mock, as there is too much stuff in it +// There has been an attempt to use a typeclass to provide the services, like +// Env: CurrentUser etc, but it was unclear how the typeclass would be used with the Dispatcher +// as it needs Env +// So for now, it is everything in one place +trait ComponentContext[+Env]: + def currentUser: Signal[Option[UserInfo]] def messages: MessageCatalogue + def modal: Modal + def dispatcher: ZIODispatcher[Env] - def nested(prefixes: String*): ComponentContext[App] = - ComponentContext.Nested[App](this, prefixes) + def nested(prefixes: String*): ComponentContext[Env] = + ComponentContext.Nested[Env](this, prefixes) object ComponentContext: case class Nested[App](parent: ComponentContext[App], prefixes: Seq[String]) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/Modal.scala b/ui/js/src/main/scala/works/iterative/ui/components/Modal.scala new file mode 100644 index 0000000..b9098da --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/Modal.scala @@ -0,0 +1,8 @@ +package works.iterative.ui.components + +import com.raquo.laminar.api.L.* + +trait Modal: + // TODO: this is wrong, we need to make it generic + def open(content: HtmlElement): Unit + def close(): Unit diff --git a/ui/js/src/main/scala/works/iterative/ui/components/ZIODispatcher.scala b/ui/js/src/main/scala/works/iterative/ui/components/ZIODispatcher.scala new file mode 100644 index 0000000..f662a20 --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/ZIODispatcher.scala @@ -0,0 +1,15 @@ +package works.iterative.ui.components + +import zio.* + +trait ZIODispatcher[+Env]: + def dispatch(action: ZIO[Env, Nothing, Unit]): Unit + +object ZIODispatcher: + def fromRuntime[Env](using runtime: Runtime[Env]): ZIODispatcher[Env] = + new ZIODispatcher[Env]: + override def dispatch(action: ZIO[Env, Nothing, Unit]): Unit = + Unsafe.unsafe(implicit unsafe => + // TODO: do I need to cancel this on evenstream stop? + val _ = runtime.unsafe.runToFuture(action) + ) 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 36fd8ea..df2cbf8 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 @@ -2,13 +2,25 @@ package ui.components import works.iterative.core.MessageCatalogue +import works.iterative.core.auth.UserInfo +import com.raquo.airstream.core.Signal -trait ComponentContext[App]: - def app: App +/** Context containing services needed in all parts of the application + */ +// TODO: this is Laminar-specific, we need to make it generic +// Also, it is hard to mock, as there is too much stuff in it +// There has been an attempt to use a typeclass to provide the services, like +// Env: CurrentUser etc, but it was unclear how the typeclass would be used with the Dispatcher +// as it needs Env +// So for now, it is everything in one place +trait ComponentContext[+Env]: + def currentUser: Signal[Option[UserInfo]] def messages: MessageCatalogue + def modal: Modal + def dispatcher: ZIODispatcher[Env] - def nested(prefixes: String*): ComponentContext[App] = - ComponentContext.Nested[App](this, prefixes) + def nested(prefixes: String*): ComponentContext[Env] = + ComponentContext.Nested[Env](this, prefixes) object ComponentContext: case class Nested[App](parent: ComponentContext[App], prefixes: Seq[String]) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/Modal.scala b/ui/js/src/main/scala/works/iterative/ui/components/Modal.scala new file mode 100644 index 0000000..b9098da --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/Modal.scala @@ -0,0 +1,8 @@ +package works.iterative.ui.components + +import com.raquo.laminar.api.L.* + +trait Modal: + // TODO: this is wrong, we need to make it generic + def open(content: HtmlElement): Unit + def close(): Unit diff --git a/ui/js/src/main/scala/works/iterative/ui/components/ZIODispatcher.scala b/ui/js/src/main/scala/works/iterative/ui/components/ZIODispatcher.scala new file mode 100644 index 0000000..f662a20 --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/ZIODispatcher.scala @@ -0,0 +1,15 @@ +package works.iterative.ui.components + +import zio.* + +trait ZIODispatcher[+Env]: + def dispatch(action: ZIO[Env, Nothing, Unit]): Unit + +object ZIODispatcher: + def fromRuntime[Env](using runtime: Runtime[Env]): ZIODispatcher[Env] = + new ZIODispatcher[Env]: + override def dispatch(action: ZIO[Env, Nothing, Unit]): Unit = + Unsafe.unsafe(implicit unsafe => + // TODO: do I need to cancel this on evenstream stop? + val _ = runtime.unsafe.runToFuture(action) + ) 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 a6663c9..bc2ffe0 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 @@ -11,6 +11,9 @@ import scala.annotation.unused import scala.scalajs.js import scala.scalajs.js.Dictionary +import works.iterative.ui.components.Modal +import works.iterative.ui.components.ZIODispatcher +import works.iterative.core.auth.UserInfo trait ScenarioMain( prefix: String, @@ -45,9 +48,14 @@ ) def main(@unused args: Array[String]): Unit = - given ComponentContext[Unit] with - val app: Unit = () + given ComponentContext[Nothing] with + val currentUser: Signal[Option[UserInfo]] = 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] = + _ => () def container: HtmlElement = div(