aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/reading/Route.scala
blob: f0984683ac1aecab619660ec18a24d70c523375d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package reading

import org.scalajs.dom.{ window, MouseEvent }
import org.scalajs.dom.raw.PopStateEvent
import scala.scalajs.js.URIUtils

import rx.Var

import reading.models.{ Filter, FilterKind, Book, Books => BooksModel }

sealed trait Route

object Route {
  case class Books(filters: Seq[Filter] = Nil, detail: Option[Book] = None) extends Route

  val current: Var[Route] = Var(parse(window.location.hash))

  window.onpopstate = (e: PopStateEvent) => {
    current() = parse(window.location.hash)
  }

  // Prevent page changes when clicking with the mouse left button
  window.onclick = { (event: MouseEvent) =>
    if (event.button == 0) event.preventDefault()
  }

  def parse(hash: String): Route =
    pathAndParams(hash) match {
      case ("books" :: Nil, params) => {
        val filters = params.flatMap {
          case Param(key, value) =>
            for {
              kind <- FilterKind.withNameOption(key)
              filter <- Filter(kind, value)
            } yield filter
          case _ =>
            None
        }
        val detail = params.collectFirst {
          case Param("detail", title) => BooksModel().find(_.title == title)
        }.flatten
        Books(filters, detail)
      }
      case _ =>
        Books()
    }

  def pathAndParams(hash: String): (List[String], List[String]) = {
    def splitPath(path: String) = path.split("/").drop(1).toList
    URIUtils.decodeURI(hash.drop(1)).split('?') match {
      case Array(path) => (splitPath(path), Nil)
      case Array(path, params) => (splitPath(path), params.split("&").toList)
    }
  }

  def url(route: Route): String = {
    val hash = route match {
      case Books(filters, detail) => {
        val filterParams = filters.map(filter => (filter.kind.toString, filter.nonFormattedName))
        val detailParams = detail.map(book => ("detail", book.title))
        s"/books${Param.format(filterParams ++ detailParams)}"
      }
      case _ => "/books"
    }
    window.location.origin + window.location.pathname + "#" + URIUtils.encodeURI(hash)
  }

  def goTo(route: Route): Unit = {
    push(route)
    current() = route
  }

  def push(route: Route): Unit = {
    window.history.pushState(null, "", url(route));
  }
}

object Param {
  def apply(key: String, value: String): (String, String) = (key, value)
  def unapply(x: Any): Option[(String, String)] =
    x match {
      case str: String =>
        str.split("=") match {
          case Array(key, value) => Some((key, value))
          case _ => None
        }
      case _ => None
    }

  def format(params: Seq[(String, String)]): String =
    if (params.isEmpty)
      ""
    else
      params
        .map { case (key, value) => s"$key=$value" }
        .mkString("?", "&", "")
}