-
-
Notifications
You must be signed in to change notification settings - Fork 57
Open
Labels
Description
Laminar does not and will not have built-in integrations with ZIO / FS2 / etc., but anyone who actually uses these libraries on the frontend can easily publish their own packages or even just share a gist. If you need help developing such a package, feel free to ask in our Discord – although I personally don't use ZIO / FS2 / etc. especially on the frontend, a few other people do.
I'm opening this ticket as a reference for any such integration examples until I have a more permanent place to put them. Please feel free to comment with more example code or links.
To start off, here is an example ZIO integration from @sherpal that he posted on discord:
package ziojs
import com.raquo.laminar.api.L.*
import zio.{Unsafe, ZIO}
package object laminarinterrop {
/** Executes asynchronously the effect when the element is mounted. */
def onMountZIO[El <: Element](zio: ZIO[services.GlobalEnv, Nothing, Unit]): Modifier[El] =
onMountZIOWithContext(_ => zio)
def onUnmountZIO[El <: Element](effect: ZIO[services.GlobalEnv, Nothing, Unit]): Modifier[El] =
onUnmountCallback(_ =>
Unsafe.unsafe { implicit unsafe =>
services.runtime.unsafe.runToFuture(effect)
()
}
)
/** Executes asynchronously the effect when the element is mounted. */
def onMountZIOWithContext[El <: Element](
effect: MountContext[El] => ZIO[services.GlobalEnv, Nothing, Unit]
): Modifier[El] =
onMountCallback[El](ctx =>
Unsafe.unsafe { implicit unsafe: Unsafe =>
services.runtime.unsafe.runToFuture(effect(ctx))
()
}
)
def onClickZIO[El <: Element](zio: ZIO[services.GlobalEnv, Throwable, Unit]): Binder[El] = {
import Implicits.ObserverEnhanced
onClick.mapTo(()) --> Observer.zio(zio)
}
}and
package ziojs.laminarinterrop
import com.raquo.laminar.api.A.*
import zio.stream.*
import zio.{CancelableFuture, URIO, Unsafe, ZIO}
import services.GlobalEnv
import scala.concurrent.Future
object Implicits {
import services.runtime
type InnerForGlobalEnv[A] = URIO[GlobalEnv, A]
implicit val zioFlattenStrategy: FlattenStrategy[Observable, InnerForGlobalEnv, EventStream] =
new FlattenStrategy[Observable, InnerForGlobalEnv, EventStream] {
def flatten[A](parent: Observable[InnerForGlobalEnv[A]]): EventStream[A] =
parent.flatMap(task =>
EventStream.fromFuture(
Unsafe.unsafe { implicit unsafe =>
services.runtime.unsafe.runToFuture(task)
},
emitFutureIfCompleted = true
)
)
}
implicit class EventStreamObjEnhanced(es: EventStream.type) {
/** Retrieve the result of the zio effect and send it through the laminar stream */
def fromZIOEffect[A](effect: ZIO[GlobalEnv, Throwable, A]): EventStream[A] =
EventStream.fromFuture(
Unsafe.unsafe { implicit unsafe =>
runtime.unsafe.runToFuture(effect)
}: Future[A],
emitFutureIfCompleted = true
)
/** Passes the outputs of the incoming [[zio.stream.ZStream]] into a laminar stream. /!\ The
* ZStream will continue to run, even after the laminar stream is over with it. The returned
* [[zio.CancelableFuture]] allows you to cancel it.
*
* I think this is not fantastic. We should do it in another way, probably.
*/
def fromZStream[A](ztream: Stream[Nothing, A]): (CancelableFuture[Unit], EventStream[A]) = {
val bus = new EventBus[A]
val f: zio.CancelableFuture[Unit] = Unsafe.unsafe { implicit unsafe =>
zio.Runtime.default.unsafe
.runToFuture(ztream.foreach(elem => ZIO.attempt(bus.writer.onNext(elem))))
}
f -> bus.events
}
}
implicit class EventStreamEnhanced[A](es: EventStream[A]) {
def flatMapZIO[B](effect: ZIO[GlobalEnv, Nothing, B]): EventStream[B] =
es.flatMap(_ => effect)
}
implicit class ObserverEnhanced(val observer: Observer.type) extends AnyVal {
def zio(effect: ZIO[GlobalEnv, Throwable, Unit]): Observer[Any] = observer.apply[Any] { _ =>
Unsafe.unsafe { implicit unsafe =>
runtime.unsafe.runToFuture(effect)
()
}
}
def zioFromFunction[A](effect: A => ZIO[GlobalEnv, Throwable, Unit]): Observer[A] =
observer[A] { elem =>
Unsafe.unsafe { implicit unsafe =>
runtime.unsafe.runToFuture(effect(elem))
()
}
}
}
implicit class RichLaminarZIO[R, E, A](val zio: ZIO[R, E, A]) extends AnyVal {
/** Takes the output of this ZIO effect and pipe it to the [[Var]], keeping the value. */
def setToVar(laminarVar: Var[A]): ZIO[R, E, A] = zio.tap(a => ZIO.succeed(laminarVar.set(a)))
}
implicit class RichVar[A](val theVar: Var[A]) extends AnyVal {
def setZIO(a: => A): ZIO[Any, Nothing, Unit] = ZIO.succeed(theVar.set(a))
}
}Reactions are currently unavailable