Skip to content

Commit

Permalink
Merge pull request #177 from armanbilge/update/fs2-dom-0.2.0-M3
Browse files Browse the repository at this point in the history
Update fs2-dom to 0.2.0-M3
  • Loading branch information
armanbilge committed Feb 6, 2023
2 parents 2962749 + c179c40 commit fbf0ae4
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 46 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ ThisBuild / tlJdkRelease := Some(8)
val CatsVersion = "2.9.0"
val CatsEffectVersion = "3.4.6"
val Fs2Version = "3.5.0"
val Fs2DomVersion = "0.2.0-M2"
val Fs2DomVersion = "0.2.0-M3"
val MonocleVersion = "3.2.0"

lazy val root =
Expand Down
7 changes: 4 additions & 3 deletions calico/src/main/scala/calico/IOWebApp.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ import calico.syntax.*
import calico.unsafe.given
import cats.effect.IO
import cats.effect.Resource
import org.scalajs.dom
import fs2.dom.Window

trait IOWebApp:

def rootElementId: String = "app"

val window: Window[IO] = Window[IO]

def render: Resource[IO, fs2.dom.HtmlElement[IO]]

def main(args: Array[String]): Unit =
val document = fs2.dom.Document[IO]
val rootElement = document.getElementById(rootElementId).map(_.get)
val rootElement = window.document.getElementById(rootElementId).map(_.get)
rootElement.flatMap(render.renderInto(_).useForever).unsafeRunAndForget()
3 changes: 3 additions & 0 deletions calico/src/main/scala/calico/html/Html.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package html
import cats.effect.IO
import cats.effect.kernel.Async
import cats.effect.kernel.Resource
import fs2.dom.Dom

object io extends Html[IO]

Expand All @@ -41,6 +42,8 @@ sealed class Html[F[_]](using F: Async[F])
KeyedChildrenModifiers[F],
HtmlAttrModifiers[F]:

given Dom[F] = Dom.forAsync

def aria: Aria[F] = Aria[F]

def cls: ClassProp[F] = ClassProp[F]
Expand Down
4 changes: 2 additions & 2 deletions docs/router.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
libraryDependencies += "com.armanbilge" %%% "calico-router" % "@VERSION@"
```

`calico-router` is built only with Cats Effect, FS2, http4s, and scala-js-dom, and has no dependency on `calico` core, such that it is framework-agnostic. Integration with **Calico** is seamless.
`calico-router` is built only with Cats Effect, FS2-DOM, and http4s, and has no dependency on `calico` core, such that it is framework-agnostic. Integration with **Calico** is seamless.

Special thanks to [@jberggg](https://github.com/jberggg) whose [fs2-spa-router](https://github.com/jberggg/fs2-spa-router) inspired this project.

Expand Down Expand Up @@ -40,7 +40,7 @@ import fs2.dom.*
import org.http4s.*
import org.http4s.syntax.all.*

val app = Resource.eval(Router(Location[IO], History[IO, Unit])).flatMap { router =>
val app = Resource.eval(Router(Window[IO])).flatMap { router =>
(SignallingRef[IO].of(0), SignallingRef[IO].of(0)).tupled.toResource.flatMap {
(helloCounter, countCounter) =>

Expand Down
14 changes: 6 additions & 8 deletions router/src/main/scala/calico/router/Router.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ import fs2.Stream
import fs2.concurrent.Signal
import fs2.concurrent.Topic
import fs2.dom.Dom
import fs2.dom.History
import fs2.dom.Location
import fs2.dom.Window
import org.http4s.Uri
import org.scalajs.dom
import org.scalajs.macrotaskexecutor.MacrotaskExecutor
Expand Down Expand Up @@ -81,10 +80,9 @@ abstract class Router[F[_]] private ():
Resource.eval(routes).flatMap(dispatch)

object Router:
def apply[F[_]: Dom](location: Location[F], history: History[F, Unit])(
using F: Async[F]): F[Router[F]] =
def apply[F[_]: Dom](window: Window[F])(using F: Async[F]): F[Router[F]] =
Topic[F, Uri].map { gps =>
val _location = location
val history = window.history[Unit]
new:
export history.{back, forward, go, length}

Expand All @@ -101,15 +99,15 @@ object Router:
yield ()

private def mkAbsolute(uri: Uri): F[Uri] =
_location.href.get.flatMap(Uri.fromString(_).liftTo).map(_.resolve(uri))
window.location.href.get.flatMap(Uri.fromString(_).liftTo).map(_.resolve(uri))

def location = new:
def get = _location.href.get.flatMap(Uri.fromString(_).liftTo[F])
def get = window.location.href.get.flatMap(Uri.fromString(_).liftTo[F])
def continuous = Stream.repeatEval(get)
def discrete = history.state.discrete.evalMap(_ => get).merge(gps.subscribe(0))

def dispatch(routes: Routes[F]) = for
container <- fs2.dom.Document[F].createElement("div").toResource
container <- window.document.createElement("div").toResource
currentRoute <- Resource.make(
F.ref(Option.empty[(Unique.Token, fs2.dom.Node[F], RefSink[F, Uri], F[Unit])]))(
_.get.flatMap(_.foldMapA(_._4)))
Expand Down
63 changes: 31 additions & 32 deletions todo-mvc/src/main/scala/todomvc/TodoMvc.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,33 +40,32 @@ import scala.collection.immutable.SortedMap

object TodoMvc extends IOWebApp:

def render = (TodoStore.make, Router(Location[IO], History[IO, Unit]).toResource).flatMapN {
(store, router) =>
router.dispatch {
Routes.one[IO] {
case uri if uri.fragment == Some("/active") => Filter.Active
case uri if uri.fragment == Some("/completed") => Filter.Completed
case _ => Filter.All
} { filter =>
def render = (TodoStore(window), Router(window).toResource).flatMapN { (store, router) =>
router.dispatch {
Routes.one[IO] {
case uri if uri.fragment == Some("/active") => Filter.Active
case uri if uri.fragment == Some("/completed") => Filter.Completed
case _ => Filter.All
} { filter =>
div(
cls := "todoapp",
div(cls := "header", h1("todos"), TodoInput(store)),
div(
cls := "todoapp",
div(cls := "header", h1("todos"), TodoInput(store)),
div(
cls := "main",
ul(
cls := "todo-list",
children[Long](id => TodoItem(store.entry(id))) <-- filter.flatMap(store.ids(_))
)
),
store
.size
.map(_ > 0)
.changes
.map(if _ then StatusBar(store.activeCount, filter, router).some else None)
)
}

cls := "main",
ul(
cls := "todo-list",
children[Long](id => TodoItem(store.entry(id))) <-- filter.flatMap(store.ids(_))
)
),
store
.size
.map(_ > 0)
.changes
.map(if _ then StatusBar(store.activeCount, filter, router).some else None)
)
}

}
}

def TodoInput(store: TodoStore) =
Expand Down Expand Up @@ -176,21 +175,21 @@ class TodoStore(entries: SignallingSortedMapRef[IO, Long, Todo], nextId: IO[Long

object TodoStore:

def make: Resource[IO, TodoStore] =
def apply(window: Window[IO]): Resource[IO, TodoStore] =
val key = "todos-calico"

for
mapRef <- SignallingSortedMapRef[IO, Long, Todo].toResource

_ <- Resource.eval {
OptionT(Storage.local[IO].getItem(key))
OptionT(window.localStorage.getItem(key))
.subflatMap(jawn.decode[SortedMap[Long, Todo]](_).toOption)
.foreachF(mapRef.set(_))
}

_ <- Storage
.local[IO]
.events
_ <- window
.localStorage
.events(window)
.foreach {
case Storage.Event.Updated(`key`, _, value, _) =>
jawn.decode[SortedMap[Long, Todo]](value).foldMapM(mapRef.set(_))
Expand All @@ -202,11 +201,11 @@ object TodoStore:

_ <- mapRef
.discrete
.foreach(todos => Storage.local[IO].setItem(key, todos.asJson.noSpaces))
.foreach(todos => window.localStorage.setItem(key, todos.asJson.noSpaces))
.compile
.drain
.backgroundOn(calico.unsafe.MacrotaskExecutor)
yield TodoStore(mapRef, IO.realTime.map(_.toMillis))
yield new TodoStore(mapRef, IO.realTime.map(_.toMillis))

case class Todo(text: String, completed: Boolean) derives Codec.AsObject

Expand Down

0 comments on commit fbf0ae4

Please sign in to comment.