Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions backend/src/braid/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,19 @@ object Main
header = "An amazing web application built with Krop"
) {

val habitRoutes =
// TODO: need idea how to store model(s) on the server side: json?
val habitRoute =
Route(
Request.post(Path / "habit" / Param.int),
Response.ok(Entity.text)
).handle(habitId => s"Response==>HabitId: $habitId")

val habitDeleteRoute =
Route(
Request.post(Path / "habit" / "delete" / Param.int),
Response.ok(Entity.text)
).handle(habitId => s"Response==>HabitId: $habitId")

val home =
Routes.home.handle(() =>
html.base(name, html.home(name, BuildInfo.kropVersion)).toString
Expand All @@ -45,7 +52,8 @@ object Main

val application =
home
.orElse(habitRoutes)
.orElse(habitRoute)
.orElse(habitDeleteRoute)
.orElse(javascript)
.orElse(assets)
.orElse(Application.notFound)
Expand Down
9 changes: 8 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ val kropVersion = "0.9.4"
val laminarVersion = "17.2.1"
val logbackVersion = "1.5.18"
val munitVersion = "1.1.1"
val Borer = "1.14.0"

// Run this command (build) to do everything involved in building the project
commands += Command.command("build") { state =>
Expand Down Expand Up @@ -93,7 +94,13 @@ lazy val frontend = project
.settings(
name := """braid-frontend""",
commonSettings,
libraryDependencies += "com.raquo" %%% "laminar" % laminarVersion,
libraryDependencies ++= Seq(
"com.raquo" %%% "laminar" % laminarVersion,
"io.bullet" %%% "borer-core" % Borer,
"io.bullet" %%% "borer-derivation" % Borer
),
// JSON codec

scalaJSUseMainModuleInitializer := true
)
.enablePlugins(ScalaJSPlugin, KropLayout)
Expand Down
1 change: 1 addition & 0 deletions date/js/src/braid/date/Date.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,5 @@ object Date {
def today(): Date = {
fromJsDate(new js.Date(js.Date.now()))
}

}
11 changes: 9 additions & 2 deletions frontend/Controller.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import braid.Habit
import braid.date.Date
import com.raquo.laminar.api.L.{_, given}

class Controller(model: Var[Map[Int, Habit]]) {
def toggleCompletionDate(id: Int, date: Date): Unit =
class Controller(model: Var[Map[String, Habit]]) {
def toggleCompletionDate(id: String, date: Date): Unit =
model.update(habits =>
habits.get(id) match {
case Some(habit) =>
Expand All @@ -16,4 +16,11 @@ class Controller(model: Var[Map[Int, Habit]]) {
case None => habits
}
)
def dropSingleRow(index: String): Unit = {
model.update(habits => habits.removed(index))
}
def sortByStreak(): Unit = {
// TODO: implement reverse sorting | Use:: inContext
model.update(habits => habits.toSeq.sortWith(_._1 > _._1).toMap)
}
}
9 changes: 1 addition & 8 deletions frontend/src/braid/Braid.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,7 @@ import scala.scalajs.js

object Braid {
def run(mount: dom.Element): Unit = {
val model =
Var(
Map(
1 -> Habit(1, "Work on Braid", 0, Seq()),
2 -> Habit(2, "Exercise", 5, Seq()),
3 -> Habit(3, "Read", 3, Seq(Date.today()))
)
)
val model = Habit.habitsVar

val controller = Controller(model)
val view = View(controller)
Expand Down
56 changes: 51 additions & 5 deletions frontend/src/braid/View.scala
Original file line number Diff line number Diff line change
@@ -1,14 +1,52 @@
package braid

import braid.date.Date
import braid.date.Day
import braid.model.Habit
import com.raquo.airstream.web.WebStorageVar
import com.raquo.laminar.api.L.{_, given}
import com.raquo.laminar.api.features.unitArrows
import com.raquo.laminar.nodes.ReactiveHtmlElement
import io.bullet.borer.Codec
import io.bullet.borer.Json
import io.bullet.borer.derivation.MapBasedCodecs._

import java.nio.charset.StandardCharsets
import scala.scalajs.js
import scala.util.Try

final case class Habit(id: Int, name: String, streak: Int, dates: Seq[Date])
final case class Habit(id: String, name: String, streak: Int, dates: Seq[Date])

object Habit {

val defaultValues = Map(
"1" -> Habit("1", "Work on Braid", 0, Seq()),
"2" -> Habit("2", "Exercise", 5, Seq()),
"3" -> Habit("3", "Read", 3, Seq(Date.today()))
)

given habitCodec: Codec[Habit] = deriveCodec
given dateCodec: Codec[Date] = deriveCodec
given dayCodec: Codec[braid.date.Day] = deriveCodec
given monthCodec: Codec[braid.date.Month] = deriveCodec

// We can use session storage instead::
// https://github.com/raquo/Airstream/?tab=readme-ov-file#sessionstorage
val habitsVar = WebStorageVar
.localStorage(
key = "habits",
syncOwner = None
)
.withCodec[Map[String, Habit]](
encode = hs => Json.encode(hs).toUtf8String,
decode = jsonStr =>
Json
.decode(jsonStr.getBytes(StandardCharsets.UTF_8))
.to[Map[String, Habit]]
.valueTry,
default = Try(defaultValues)
)
}

class View(controller: Controller) {
def last7Days: Seq[Date] = {
Expand All @@ -35,7 +73,7 @@ class View(controller: Controller) {
)
}

def view(habits: Signal[Map[Int, Habit]]): Div =
def view(habits: Signal[Map[String, Habit]]): Div =
div(
className := "bg-white rounded-lg shadow-lg overflow-hidden",
div(
Expand All @@ -51,7 +89,12 @@ class View(controller: Controller) {
),
th(
className := "px-4 py-4 text-center text-sm font-semibold text-gray-700",
"Streak"
"Streak",
onClick --> {
// TODO: sort by streak
println("Sorting ...")
controller.sortByStreak()
}
),
last7DaysTableHeadings,
th(className := "px-4 py-4")
Expand Down Expand Up @@ -105,8 +148,11 @@ class View(controller: Controller) {
className := "px-4 py-4 text-center",
button(
"Delete",
onClick.flatMap(_ => FetchStream.post("/habit/" + habit.id)) --> {
responseText => println(responseText)
onClick.flatMap(_ =>
FetchStream.post("/habit/delete/" + habit.id)
) --> { responseText =>
// println(responseText) || assume its a success response
controller.dropSingleRow(habit.id)
},
className := "text-red-500 hover:text-red-700 transition"
)
Expand Down