diff --git a/README.md b/README.md index 945410e8c2ea5ac2cff9c605f748c75574446217..1645fe1342a78b9e80201593aff0898f605d3210 100644 --- a/README.md +++ b/README.md @@ -2,159 +2,4 @@ This is a library that provides building blocks for cs-214 webapps. -### Example - -This example shows a simple counter webapp built using the `webapp-lib`. - -An example webapp is implemented in 4 parts: - -#### Counter Model (types) - -Those include the definition of state, events and view. - -```scala -package apps.counter - -case class CounterView(value: Int) - -case class CounterState(value: Int) - -enum CounterEvent: - case Increment -``` - -#### Counter wire - -The serialization and deserialization of the events and view. - -```scala -package apps.counter - -import cs214.webapp.* -import cs214.webapp.DecodingException - -import scala.util.{Failure, Success, Try} - -object CounterWire extends AppWire[CounterEvent, CounterView]: - import CounterEvent.* - import CounterView.* - import ujson.* - - override object eventFormat extends WireFormat[CounterEvent]: - override def encode(event: CounterEvent): Value = - event match - case Increment => - Obj("type" -> "Increment") - - override def decode(js: Value): Try[CounterEvent] = - Try: - js("type").str match - case "Increment" => - Increment - case _ => - throw DecodingException(f"Invalid counter event $js") - - override object viewFormat extends WireFormat[CounterView]: - override def encode(v: CounterView): Value = - Obj("value" -> v.value) - - override def decode(js: Value): Try[CounterView] = - Try: - CounterView(js("value").num.toInt) -``` - -#### Counter logic - -The logic of the counter -- a transition system that updates the state based on the events and can project a state into a view. - -```scala -package apps.counter - -import cs214.webapp.* -import cs214.webapp.server.{StateMachine} -import scala.util.{Failure, Success, Try} - -class CounterLogic extends StateMachine[CounterEvent, CounterState, CounterView]: - import Action.* - - override val appInfo: AppInfo = AppInfo( - id = "counter", - name = "Counter", - description = "A simple counter app that increments a counter by one.", - year = 2024 - ) - - override val wire = CounterWire - - override def init(clients: Seq[UserId]): CounterState = CounterState(0) - - override def transition(state: CounterState)(userId: UserId, event: CounterEvent): Try[Seq[Action[CounterState]]] = - Try: - event match - case CounterEvent.Increment => - Seq(Render(CounterState(state.value + 1))) - - override def project(state: CounterState)(userId: UserId): CounterView = - CounterView(state.value) -``` - -#### Counter UI - -The UI representation of the counter view. - -```scala -package apps.counter - -import apps.counter.* -import cs214.webapp.* -import cs214.webapp.client.* -import cs214.webapp.client.graphics.{HTMLAttribute, TextClientAppInstance} -import org.scalajs.dom -import scalatags.JsDom.all.* - -import scala.scalajs.js.annotation.JSExportTopLevel - -@JSExportTopLevel("counter") -object CounterClientApp extends WSClientApp: - def appId: String = "counter" - - def uiId: UIId = "counter" - - def init(userId: UserId, sendMessage: ujson.Value => Unit, target: dom.Element): ClientAppInstance = - CounterClientAppInstance(userId, sendMessage, target) - -class CounterClientAppInstance(userId: UserId, sendMessage: ujson.Value => Unit, target: dom.Element) - extends TextClientAppInstance[CounterEvent, CounterView](userId, sendMessage, target): - - override val wire = CounterWire - - override def handleTextInput(view: CounterView, text: String): Option[CounterEvent] = text match - case "increment" | "i" => Some(CounterEvent.Increment) - case _ => None - - override def renderView(userId: UserId, view: CounterView): Vector[TextSegment] = - Vector( - TextSegment(text = "Counter\n", modifiers = cls := "bold"), - TextSegment("Click the number to increment it!\n"), - TextSegment( - view.value.toString, - onMouseEvent = { - case MouseEvent.Click(_) => sendEvent(CounterEvent.Increment) - case _ => () - }, - modifiers = cls := "bold clickable huge" - ) - ) - - override def css: String = - """|.bold { - | font-weight: bold; - |} - |.clickable { - | cursor: pointer; - |} - |.huge { - | font-size: 2em; - |} - |""".stripMargin -``` +For a minimal example on how to use the library see the [`counter` example](https://cs-214.epfl.ch/labs/webapp-examples-2d859dd07e39/counter.html)