Last active
March 11, 2023 17:59
-
-
Save fredshonorio/4aa4b2e5995b771874d5a348b134a2ad to your computer and use it in GitHub Desktop.
remote thingy for calico
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package remote | |
import cats.effect.* | |
import cats.syntax.all.* | |
import fs2.dom.* | |
import calico.* | |
import calico.html.io.{*, given} | |
import fs2.concurrent.* | |
import fs2.* | |
sealed trait Remote[+A] | |
object Remote { | |
case object NotRequested extends Remote[Nothing] | |
case object Loading extends Remote[Nothing] | |
case class Loaded[A](value: A) extends Remote[A] | |
case class Error(cause: Throwable) extends Remote[Nothing] | |
} | |
trait RemoteRequest[REQ, RES] { | |
def responses: Signal[IO, Remote[RES]] | |
def request(req: REQ): IO[Unit] | |
} | |
object RemoteRequest { | |
def build[REQ, RES](action: REQ => IO[RES]): Resource[IO, RemoteRequest[REQ, RES]] = | |
Resource | |
.eval(Channel.unbounded[IO, REQ]) | |
.flatMap { channel => | |
val signal = channel.stream | |
.flatMap[IO, Remote[RES]](arg => | |
Stream.eval(IO.pure(Remote.Loading: Remote[RES])) ++ | |
Stream.eval(action(arg).map(Remote.Loaded(_)).recover(Remote.Error(_))) | |
) | |
.holdResource(Remote.NotRequested) | |
signal.map(sig => | |
new RemoteRequest[REQ, RES] { | |
val responses: Signal[IO, Remote[RES]] = sig | |
def request(req: REQ): IO[Unit] = channel.send(req).void | |
} | |
) | |
} | |
def example: Resource[IO, HtmlElement[IO]] = { | |
import scala.concurrent.duration.* | |
import cats.effect.std.Random | |
Random | |
.scalaUtilRandom[IO] | |
.toResource | |
.flatMap { rng => | |
val req = (i: Int) => | |
rng | |
.betweenInt(0, 10) | |
.flatMap(chance => | |
if (chance > 5) IO.sleep(1.second) >> IO.pure(i.toString) | |
else IO.raiseError(new RuntimeException("bad")) | |
) | |
RemoteRequest.build(req).flatMap { remote => | |
div( | |
button( | |
cls := "button", | |
"do it", | |
onClick --> (_.foreach(_ => rng.betweenInt(0, 10).flatMap(remote.request))) | |
), | |
remote.responses.map { | |
case Remote.NotRequested => div("") | |
case Remote.Loading => div("loading") | |
case Remote.Loaded(value) => div(value) | |
case Remote.Error(cause) => div(cause.getMessage) | |
} | |
) | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment