Skip to content

Commit

Permalink
Finished version 6 using Scala.js for the client-side.
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkCLewis committed Apr 12, 2020
1 parent de2c552 commit 3814fed
Show file tree
Hide file tree
Showing 13 changed files with 272 additions and 17 deletions.
11 changes: 0 additions & 11 deletions client/src/main/scala/edu/trinity/webapps/ScalaJSExample.scala

This file was deleted.

49 changes: 49 additions & 0 deletions client/src/main/scala/playscala/FetchJson.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package playscala

import play.api.libs.json.JsError
import org.scalajs.dom.experimental.Headers
import org.scalajs.dom.experimental.Fetch
import org.scalajs.dom.experimental.HttpMethod
import org.scalajs.dom.experimental.RequestInit
import play.api.libs.json.Json
import play.api.libs.json.JsSuccess
import play.api.libs.json.Writes
import play.api.libs.json.Reads
import scala.scalajs.js.Thenable.Implicits._
import scala.concurrent.ExecutionContext


object FetchJson {
def fetchPost[A, B](url: String, csrfToken: String,
data: A, success: B => Unit, error: JsError => Unit)(implicit
writes: Writes[A], reads: Reads[B], ec: ExecutionContext): Unit = {
val headers = new Headers()
headers.set("Content-Type", "application/json")
headers.set("Csrf-Token", csrfToken)
Fetch.fetch(url, RequestInit(method = HttpMethod.POST,
headers = headers, body = Json.toJson(data).toString))
.flatMap(res => res.text())
.map { data =>
Json.fromJson[B](Json.parse(data)) match {
case JsSuccess(b, path) =>
success(b)
case e @ JsError(_) =>
error(e)
}
}
}

def fetchGet[B](url: String, success: B => Unit, error: JsError => Unit)(implicit
reads: Reads[B], ec: ExecutionContext): Unit = {
Fetch.fetch(url)
.flatMap(res => res.text())
.map { data =>
Json.fromJson[B](Json.parse(data)) match {
case JsSuccess(b, path) =>
success(b)
case e @ JsError(_) =>
error(e)
}
}
}
}
19 changes: 19 additions & 0 deletions client/src/main/scala/playscala/ScalaJSExample.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package playscala

import shared.SharedMessages
import org.scalajs.dom
import org.scalajs.dom.html

object ScalaJSExample {

def main(args: Array[String]): Unit = {
println("This is in Scala.js.")
if (dom.document.getElementById("scalajsShoutOut") != null) {
dom.document.getElementById("scalajsShoutOut").textContent = SharedMessages.itWorks
}

if (dom.document.getElementById("version6") != null) {
Version6.init()
}
}
}
136 changes: 136 additions & 0 deletions client/src/main/scala/playscala/Version6.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package playscala

import org.scalajs.dom
import org.scalajs.dom.document
import org.scalajs.dom.html
import org.scalajs.dom.experimental.Fetch
import org.scalajs.dom.experimental.Headers
import org.scalajs.dom.experimental.RequestInit
import org.scalajs.dom.experimental.HttpMethod
import org.scalajs.dom.experimental.RequestMode
import play.api.libs.json.Json
import models.ReadsAndWrites._
import scala.scalajs.js.Thenable.Implicits._
import play.api.libs.json.JsSuccess
import play.api.libs.json.JsError
import scala.concurrent.ExecutionContext
import scala.scalajs.js.annotation.JSExportTopLevel
import scalajs.js
import models.TaskItem

object Version6 {
implicit val ec = ExecutionContext.global

val csrfToken = document.getElementById("csrfToken").asInstanceOf[html.Input].value
val validateRoute = document.getElementById("validateRoute").asInstanceOf[html.Input].value
val tasksRoute = document.getElementById("tasksRoute").asInstanceOf[html.Input].value
val createRoute = document.getElementById("createRoute").asInstanceOf[html.Input].value
val deleteRoute = document.getElementById("deleteRoute").asInstanceOf[html.Input].value
val addRoute = document.getElementById("addRoute").asInstanceOf[html.Input].value
val logoutRoute = document.getElementById("logoutRoute").asInstanceOf[html.Input].value

def init(): Unit = {
println("In version 6.")
}

@JSExportTopLevel("login")
def login(): Unit = {
val username = document.getElementById("loginName").asInstanceOf[html.Input].value
val password = document.getElementById("loginPass").asInstanceOf[html.Input].value
val data = models.UserData(username, password)
FetchJson.fetchPost(validateRoute, csrfToken, data, (bool: Boolean) => {
if(bool) {
document.getElementById("login-section").asInstanceOf[js.Dynamic].hidden = true
document.getElementById("task-section").asInstanceOf[js.Dynamic].hidden = false
document.getElementById("login-message").innerHTML = ""
document.getElementById("create-message").innerHTML = ""
document.getElementById("loginName").asInstanceOf[html.Input].value = ""
document.getElementById("loginPass").asInstanceOf[html.Input].value = ""
loadTasks()
} else {
document.getElementById("login-message").innerHTML = "Login Failed"
}
}, e => {
println("Fetch error: " + e)
})
}

@JSExportTopLevel("createUser")
def createUser(): Unit = {
val username = document.getElementById("createName").asInstanceOf[html.Input].value
val password = document.getElementById("createPass").asInstanceOf[html.Input].value
val data = models.UserData(username, password)
FetchJson.fetchPost(createRoute, csrfToken, data, (bool: Boolean) => {
if(bool) {
document.getElementById("login-section").asInstanceOf[js.Dynamic].hidden = true
document.getElementById("task-section").asInstanceOf[js.Dynamic].hidden = false
document.getElementById("login-message").innerHTML = ""
document.getElementById("create-message").innerHTML = ""
document.getElementById("createName").asInstanceOf[html.Input].value = ""
document.getElementById("createPass").asInstanceOf[html.Input].value = ""
loadTasks()
} else {
document.getElementById("create-message").innerHTML = "User Creation Failed"
}
}, e => {
println("Fetch error: " + e)
})
}

def loadTasks(): Unit = {
val ul = document.getElementById("task-list")
ul.innerHTML = ""
FetchJson.fetchGet(tasksRoute, (tasks: Seq[TaskItem]) => {
for (task <- tasks) {
val li = document.createElement("li")
val text = document.createTextNode(task.text)
li.appendChild(text);
li.asInstanceOf[html.LI].onclick = e => {
delete(task.id)
}
ul.appendChild(li)
}
}, e => {
println("Fetch error: " + e)
})
}

def delete(id: Int): Unit = {
FetchJson.fetchPost(deleteRoute, csrfToken, id, (bool: Boolean) => {
if(bool) {
document.getElementById("task-message").innerHTML = ""
loadTasks()
} else {
document.getElementById("task-message").innerHTML = "Failed to delete."
}
}, e => {
println("Fetch error: " + e)
})
}

@JSExportTopLevel("addTask")
def addTask(): Unit = {
val task = document.getElementById("newTask").asInstanceOf[html.Input].value
FetchJson.fetchPost(addRoute, csrfToken, task, (bool: Boolean) => {
if(bool) {
loadTasks()
document.getElementById("newTask").asInstanceOf[html.Input].value = ""
document.getElementById("task-message").innerHTML = ""
} else {
document.getElementById("task-message").innerHTML = "Failed to add."
}
}, e => {
println("Fetch error: " + e)
})
}

@JSExportTopLevel("logout")
def logout(): Unit = {
FetchJson.fetchGet(logoutRoute, (bool: Boolean) => {
document.getElementById("login-section").asInstanceOf[js.Dynamic].hidden = false;
document.getElementById("task-section").asInstanceOf[js.Dynamic].hidden = true;
}, e => {
println("Fetch error: " + e)
})
}
}
2 changes: 1 addition & 1 deletion project/metals.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// DO NOT EDIT! This file is auto-generated.
// This file enables sbt-bloop to create bloop config files.

addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.0-RC1-105-118a551b")
addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.0-RC1-190-ef7d8dba")
1 change: 1 addition & 0 deletions server/app/controllers/TaskList5.scala
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class TaskList5 @Inject() (protected val dbConfigProvider: DatabaseConfigProvide

def taskList = Action.async { implicit request =>
withSessionUsername { username =>
println("!!! Getting tasks")
model.getTasks(username).map(tasks => Ok(Json.toJson(tasks)))
}
}
Expand Down
16 changes: 16 additions & 0 deletions server/app/controllers/TaskList6.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package controllers

import javax.inject._

import play.api.mvc._
import play.api.i18n._
import models.TaskListInMemoryModel
import play.api.libs.json._
import models._

@Singleton
class TaskList6 @Inject() (cc: ControllerComponents) extends AbstractController(cc) {
def load = Action { implicit request =>
Ok(views.html.version6Main())
}
}
4 changes: 0 additions & 4 deletions server/app/models/UserData.scala

This file was deleted.

2 changes: 1 addition & 1 deletion server/app/views/main.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@
@flash.get("error")
@flash.get("success")
@content
@scalajs.html.scripts("client", routes.Assets.versioned(_).toString, name => getClass.getResource(s"/public/$name") != null)
@scalajs.html.scripts("play-videos-client", routes.Assets.versioned(_).toString, name => getClass.getResource(s"/public/$name") != null)
</body>
</html>
46 changes: 46 additions & 0 deletions server/app/views/version6Main.scala.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
@()(implicit request: RequestHeader, flash: Flash)

@main("Task List 6"){
<div id="contents">Version 6</div>

<div id="login-section">
<h2>Login:</h2>
<br>
Username: <input type="text" id="loginName">
<br>
Password: <input type="password" id="loginPass">
<br>
<button onclick="login()">Login</button><span id="login-message"></span>

<h2>Create User:</h2>
<br>
Username: <input type="text" id="createName">
<br>
Password: <input type="password" id="createPass">
<br>
<button onclick="createUser()">Create User</button><span id="create-message"></span>
</div>

<div id="task-section" hidden>
<h2>Task List</h2>

<ul id="task-list">
</ul>

<input type="text" id="newTask"></input>
<button onclick="addTask()">Add Task</button><span id="task-message"></span>

<div>
<button onclick="logout()">Logout</button>
</div>
</div>

<input type="hidden" id="version6" value="">
<input type="hidden" id="csrfToken" value="@helper.CSRF.getToken.value">
<input type="hidden" id="validateRoute" value="@routes.TaskList5.validate()">
<input type="hidden" id="tasksRoute" value="@routes.TaskList5.taskList()">
<input type="hidden" id="createRoute" value="@routes.TaskList5.createUser()">
<input type="hidden" id="deleteRoute" value="@routes.TaskList5.delete()">
<input type="hidden" id="addRoute" value="@routes.TaskList5.addTask()">
<input type="hidden" id="logoutRoute" value="@routes.TaskList5.logout()">
}
3 changes: 3 additions & 0 deletions server/conf/routes
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ POST /addTask5 controllers.TaskList5.addTask
POST /deleteTask5 controllers.TaskList5.delete
GET /logout5 controllers.TaskList5.logout

#Routes for version 6
GET /load6 controllers.TaskList6.load


# Prefix must match `play.assets.urlPrefix`
GET /assets/*file controllers.Assets.at(file)
Expand Down
Binary file removed shared/src/main/scala/shared/SharedMessages$.class
Binary file not shown.
Binary file removed shared/src/main/scala/shared/SharedMessages.class
Binary file not shown.

0 comments on commit 3814fed

Please sign in to comment.