Skip to content

Commit

Permalink
added custom router build on 3rd party library
Browse files Browse the repository at this point in the history
  • Loading branch information
DeamonDev authored and Piotr Rudnicki committed May 23, 2023
1 parent ee22edb commit 008137f
Showing 1 changed file with 120 additions and 53 deletions.
173 changes: 120 additions & 53 deletions examples/routing/src/main/scala/example/Main.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package myorg

import cats.effect.IO
import myorg.Page.getPage
import org.scalajs.dom
import tyrian.Html.{param => _, _}
import tyrian.*
import urldsl.errors.SimplePathMatchingError
import urldsl.language.PathSegment
import urldsl.language.PathSegmentWithQueryParams
import urldsl.language.simpleErrorImpl.*
import urldsl.vocabulary.Param
import urldsl.vocabulary.Segment
Expand All @@ -14,54 +18,46 @@ import scala.scalajs.js.annotation.JSExportTopLevel
@JSExportTopLevel("TyrianApp")
object HelloTyrian extends TyrianApp[Msg, Model]:

val homePath = root / "home"
val counterPath = root / "counter"
val moreComplexPath = root / "id" / segment[Int]
val pathWithParam = (root / "user" / endOfSegments) ? param[Int]("age").?

private def getPageFromURL(path: String): Page =
homePath
.matchRawUrl(path)
.fold(
_ =>
counterPath
.matchRawUrl(path)
.fold(
_ =>
moreComplexPath
.matchRawUrl(path)
.fold(
_ =>
pathWithParam
.matchRawUrl(path)
.fold(
_ => Page.NotFound,
{ case UrlMatching(_, ageOption) =>
Page.UserAgePage(ageOption)
}
),
userId => Page.UserPage(userId)
),
_ => Page.Counter
),
_ => Page.Home
)

def init(flags: Map[String, String]): (Model, Cmd[IO, Msg]) =
(
AppState(getPageFromURL(dom.window.location.href), counter = 0),
AppState(
dom.window.location.href.getPage(),
counter = 0
),
Cmd.None
)

def update(model: Model): Msg => (Model, Cmd[IO, Msg]) =
case Msg.Increment => (model.copy(counter = model.counter + 1), Cmd.None)
case Msg.Decrement => (model.copy(counter = model.counter - 1), Cmd.None)
case Msg.Reset => (model.copy(counter = 0), Cmd.None)
case Msg.GoToPage(page) =>
(model.copy(page = page), Cmd.SideEffect(dom.window.history.replaceState({}, "TyrianApp", Page.toString(page))))
case Msg.Void => (model, Cmd.None)
case Msg.GoToCounterPage =>
val newModel = model.copy(page = Page.Counter)

(
newModel,
Cmd.SideEffect(
dom.window.history
.replaceState({}, "Counter", newModel.page.asString())
)
)
case Msg.GoToHomePage =>
val newModel = model.copy(page = Page.Home)

(
newModel,
Cmd.SideEffect(
dom.window.history
.replaceState({}, "Home", newModel.page.asString())
)
)

def viewHome(model: Model): Html[Msg] =
div(p("Hello from home!"))
div(
p("Hello from home!"),
button(onClick(Msg.GoToCounterPage))("Go counter")
)

def viewCounter(model: Model): Html[Msg] =
div(`class` := "flex flex-col justify-center items-center")(
Expand All @@ -87,17 +83,27 @@ object HelloTyrian extends TyrianApp[Msg, Model]:
else
button(
`class` := "bg-red-300 text-gray-700 font-bold py-2 px-4 rounded mt-2 cursor-not-allowed"
)("Reset")
)("Reset"),
button(onClick(Msg.GoToHomePage))("Go to homepage")
)

def viewNotFound(): Html[Msg] =
div(p("Not found the requested page"), button(onClick(Msg.GoToPage(Page.Home)))("Go Home"))
div(
p("Not found the requested page"),
button(onClick(Msg.GoToHomePage))("Go to homepage")
)

def viewUserPage(userId: Int): Html[Msg] =
div(p(s"Welcome at user page with user id: ${userId}"))
div(
p(s"Welcome at user page with user id: ${userId}"),
button(onClick(Msg.GoToHomePage))("Go to home")
)

def viewUserMaybeAgePage(ageOption: Option[Int]): Html[Msg] =
ageOption.fold(div(p("You're so boring...")))(age => div(p(s"Thanks, you are cool and have $age years!")))
ageOption.fold(div(p("You're so boring...")))(age =>
div(p(s"Thanks, you are cool and have $age years!"))
)


def view(model: Model): Html[Msg] =
model.page match {
Expand All @@ -117,20 +123,79 @@ enum Page {
case NotFound
case UserPage(userId: Int)
case UserAgePage(ageOption: Option[Int])

}

object Page {
def toString(p: Page) = p match {
case Home => "/home"
case Counter => "/counter"
case UserPage(userId) => s"/id/${userId}"
case UserAgePage(ageOption) =>
ageOption match {
case None => "/user"
case Some(age) => s"/user?age=${age}"
}
case _ => ???
}
val homePath = root / "home"
val counterPath = root / "counter"
val moreComplexPath = root / "id" / segment[Int]
val pathWithParam = (root / "user" / endOfSegments) ? param[Int]("age").?

case class SimpleRoute[X, P](
pathSegment: PathSegment[X, SimplePathMatchingError],
combinator: X => P
)

case class ComplexRoute[X, P](
pathSegment: PathSegmentWithQueryParams[?, SimplePathMatchingError, X, ?],
combinator: UrlMatching[?, X] => P
)

def routerFromList(
xs: List[SimpleRoute[?, Page] | ComplexRoute[?, Page]],
path: String
): Page =
xs match {
case Nil => Page.NotFound
case (head: SimpleRoute[?, Page]) :: Nil =>
head.pathSegment
.matchRawUrl(path)
.fold[Page](_ => Page.NotFound, head.combinator(_))
case (head: ComplexRoute[?, Page]) :: Nil =>
head.pathSegment
.matchRawUrl(path)
.fold[Page](_ => Page.NotFound, head.combinator(_))
case (head: SimpleRoute[?, Page]) :: tail =>
head.pathSegment
.matchRawUrl(path)
.fold[Page](_ => routerFromList(tail, path), head.combinator(_))
case (head: ComplexRoute[?, Page]) :: tail =>
head.pathSegment
.matchRawUrl(path)
.fold[Page](_ => routerFromList(tail, path), head.combinator(_))
}

extension (p: Page)
def asString(): String = p match {
case Counter => "/counter"
case Home => "/home"
case NotFound => "/notfound"
case UserPage(userId) => s"/id/${userId}"
case UserAgePage(ageOption) =>
ageOption match {
case None => "/user"
case Some(age) => s"/user?age=${age}"
}
}

extension (path: String)
def getPage(): Page = routerFromList(
List(
SimpleRoute[Unit, Page](homePath, _ => Page.Home),
SimpleRoute[Unit, Page](counterPath, _ => Page.Counter),
SimpleRoute[Int, Page](
moreComplexPath,
(userId: Int) => Page.UserPage(userId)
),
ComplexRoute[Option[Int], Page](
pathWithParam,
{ case UrlMatching(_, ageOption) => Page.UserAgePage(ageOption) }
)
),
path
)

}

case class AppState(page: Page, counter: Int)
Expand All @@ -139,4 +204,6 @@ type Model = AppState

enum Msg:
case Increment, Decrement, Reset
case GoToPage(p: Page)
case Void
case GoToCounterPage
case GoToHomePage

0 comments on commit 008137f

Please sign in to comment.