diff --git a/js/src/main/scala/cs214/webapp/client/Pages.scala b/js/src/main/scala/cs214/webapp/client/Pages.scala index d70fb37c670bf4acefc9d4323a3019c100157a54..9603741e3c0c1dd4c81721325a4e1afbad05547b 100644 --- a/js/src/main/scala/cs214/webapp/client/Pages.scala +++ b/js/src/main/scala/cs214/webapp/client/Pages.scala @@ -1,7 +1,7 @@ package cs214.webapp package client -import java.net.{URLDecoder, URLEncoder} +import java.net.URLEncoder import scala.concurrent.ExecutionContext.Implicits.global import org.scalajs.dom import org.scalajs.dom.{Element, KeyCode} @@ -21,45 +21,9 @@ abstract class Page: target.replaceChildren(frag.render) target.setAttribute("class", classList) - private def path: Seq[String] = - this match - case HomePage => - Nil - case InstanceCreationPage(appId) => - List("app", appId) - case UIPage(appId, instanceId) => - List("app", appId, instanceId) - case JoinPage(appId, instanceId, uiId) => - List("app", appId, instanceId, uiId) - case AppPage(appId, instanceId, uiId, userId) => - List("app", appId, instanceId, uiId, userId) - case _ => - throw IllegalArgumentException(f"No path for $this!") - - def url = - f"/${path.map(s => URLEncoder.encode(s, "UTF-8")).mkString("/")}" - object Page: - /** Creates a component from a URL path - * @param appInfos - * retrieves the app information given its id - */ - def from(url: String): Page = - val components = url.stripPrefix("/").stripSuffix("/").split("/") - val decoded = components.map(c => URLDecoder.decode(c, "UTF-8")).toList - decoded match - case List("") => - HomePage - case List("app", appId) => - InstanceCreationPage(appId) - case List("app", appId, instanceId) => - UIPage(appId, instanceId) - case List("app", appId, instanceId, uiId) => - JoinPage(appId, instanceId, uiId) - case List("app", appId, instanceId, uiId, userId) => - AppPage(appId, instanceId, uiId, userId) - case _ => - throw IllegalArgumentException(f"Unknown url $url!") + @deprecated("Use WebClient.route") + def from(url: String): Page = WebClient.route(url) /** The initial home page which fetches the list of apps */ object HomePage extends Page: diff --git a/js/src/main/scala/cs214/webapp/client/Router.scala b/js/src/main/scala/cs214/webapp/client/Router.scala new file mode 100644 index 0000000000000000000000000000000000000000..103b8174d4ead30cae0884a6252199f469f4833f --- /dev/null +++ b/js/src/main/scala/cs214/webapp/client/Router.scala @@ -0,0 +1,46 @@ +package cs214.webapp +package client + +import java.net.{URLDecoder, URLEncoder} + +abstract class Router: + protected def path(page: Page): Seq[String] + protected def page(path: Seq[String]): Page + + def url(page: Page): String = + val fragments = path(page).map(s => URLEncoder.encode(s, "UTF-8")) + ("" +: fragments).mkString("/") + + def route(url: String): Page = + val components = url.stripPrefix("/").stripSuffix("/").split("/") + val decoded = components.map(c => URLDecoder.decode(c, "UTF-8")).toList + page(decoded) + +object DefaultRouter extends Router: + def path(page: Page): Seq[String] = page match + case HomePage => + List("") + case InstanceCreationPage(appId) => + List("app", appId) + case UIPage(appId, instanceId) => + List("app", appId, instanceId) + case JoinPage(appId, instanceId, uiId) => + List("app", appId, instanceId, uiId) + case AppPage(appId, instanceId, uiId, userId) => + List("app", appId, instanceId, uiId, userId) + case _ => + throw IllegalArgumentException(f"No path for $this!") + + def page(path: List[String]): Page = path match + case List("") => + HomePage + case List("app", appId) => + InstanceCreationPage(appId) + case List("app", appId, instanceId) => + UIPage(appId, instanceId) + case List("app", appId, instanceId, uiId) => + JoinPage(appId, instanceId, uiId) + case List("app", appId, instanceId, uiId, userId) => + AppPage(appId, instanceId, uiId, userId) + case _ => + throw IllegalArgumentException(f"Unknown url $url!") diff --git a/js/src/main/scala/cs214/webapp/client/WebClient.scala b/js/src/main/scala/cs214/webapp/client/WebClient.scala index d4ae99e10be0b304e732ea76a9d5eee7478a4cf2..6ec1ee235a8b6aa630ab29a868b74af23932e64d 100644 --- a/js/src/main/scala/cs214/webapp/client/WebClient.scala +++ b/js/src/main/scala/cs214/webapp/client/WebClient.scala @@ -8,7 +8,13 @@ import org.scalajs.dom object WebClient: private val appLibrary: mutable.Map[AppId, Seq[ClientApp]] = mutable.Map.empty - val getApps = (appId: AppId) => appLibrary.getOrElse(appId, Seq.empty[ClientApp]) + private var router: Router = DefaultRouter + def setRouter(router: Router): Unit = this.router = router + def route(url: String): Page = router.route(url) + def url(page: Page): String = router.url(page) + + def getApps(appId: AppId) = + appLibrary.getOrElse(appId, Seq.empty[ClientApp]) def register(clientApp: ClientApp): Unit = println(s"Registered ${clientApp.appId}/${clientApp.uiId}'s UI") @@ -20,14 +26,12 @@ object WebClient: def start(root: dom.Element): Unit = println(s"Registered apps: ${appLibrary.keys.toSeq.sorted}.") - try - Page.from(dom.document.location.pathname) - .renderInto(root) + try router.route(dom.document.location.pathname).renderInto(root) catch case t => crash(t) def navigateTo(page: Page, overwriteHistory: Boolean = false) = - if overwriteHistory then dom.window.location.replace(page.url) - else dom.window.location.assign(page.url) + if overwriteHistory then dom.window.location.replace(url(page)) + else dom.window.location.assign(url(page)) def crash(t: Throwable) = dom.document.querySelector("body").prepend: