WIP
This commit is contained in:
parent
263a609084
commit
40ea76895e
10 changed files with 149 additions and 36 deletions
|
|
@ -4,7 +4,7 @@
|
|||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev": "vite --host",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -12,10 +12,16 @@ import scala.concurrent.Future
|
|||
import scala.scalajs.js
|
||||
|
||||
import model.Entry
|
||||
import model.EntryId
|
||||
import rdts.base.Lattice
|
||||
import rdts.datatypes.LastWriterWins
|
||||
import scala.scalajs.js.Date
|
||||
import scala.util.Failure
|
||||
import scala.util.Success
|
||||
|
||||
object DexieDB {
|
||||
|
||||
private val schemaVersion = 1.0
|
||||
private val schemaVersion = 1.1
|
||||
|
||||
private val dexieDB: Dexie = new Dexie.^("fahrtenbuch")
|
||||
dexieDB
|
||||
|
|
@ -31,9 +37,62 @@ object DexieDB {
|
|||
val entriesObservable: Observable[Future[Seq[Entry]]] =
|
||||
liveQuery(() => getAllEntries())
|
||||
|
||||
def insertEntry(entry: Entry): Future[Any] = {
|
||||
println(s"inserting Entry $entry")
|
||||
def getEntry(id: EntryId): Future[Option[Entry]] =
|
||||
entriesTable
|
||||
.get(id.delegate)
|
||||
.toFuture
|
||||
.map(_.toOption.map(NativeConverter[Entry].fromNative(_)))
|
||||
|
||||
def insertEntry(entry: Entry): Unit = {
|
||||
val e: Future[Option[Entry]] = getEntry(entry.id)
|
||||
e.flatMap {
|
||||
case Some(oldEntry) =>
|
||||
println(s"found old: $oldEntry")
|
||||
println(s"found new: $entry")
|
||||
println(oldEntry.id == entry.id)
|
||||
val newEntry = Lattice[Entry].merge(entry, entry)
|
||||
val newEntry2 = Lattice[Entry].merge(oldEntry, oldEntry)
|
||||
println(oldEntry.id)
|
||||
println(entry.id)
|
||||
val test =
|
||||
Lattice[Entry].merge(
|
||||
Entry(
|
||||
EntryId("1"),
|
||||
LastWriterWins.now(0),
|
||||
LastWriterWins.now(2),
|
||||
LastWriterWins.now(""),
|
||||
LastWriterWins.now(false),
|
||||
LastWriterWins.now("Dirk"),
|
||||
LastWriterWins.now(new Date())
|
||||
),
|
||||
Entry(
|
||||
EntryId("1"),
|
||||
LastWriterWins.now(0),
|
||||
LastWriterWins.now(2),
|
||||
LastWriterWins.now(""),
|
||||
LastWriterWins.now(false),
|
||||
LastWriterWins.now("Dirk"),
|
||||
LastWriterWins.now(new Date())
|
||||
)
|
||||
)
|
||||
val newEntry3 =
|
||||
Lattice[Entry].merge(oldEntry, entry.copy(id = oldEntry.id))
|
||||
println("yolo")
|
||||
Future.unit
|
||||
// entriesTable.put(newEntry.toNative).toFuture
|
||||
case _ =>
|
||||
entriesTable.put(entry.toNative).toFuture
|
||||
}.onComplete {
|
||||
case Failure(exception) => println(s"failed with $exception")
|
||||
case Success(value) => ()
|
||||
}
|
||||
// .toFuture
|
||||
// .map(e =>
|
||||
// if e.isUndefined then entriesTable.put(entry.toNative).toFuture
|
||||
// else
|
||||
// val dbEntry = NativeConverter[Entry].fromNative(e)
|
||||
// Lattice[Entry].merge(entry, dbEntry)
|
||||
// )
|
||||
}
|
||||
|
||||
def getAllEntries(): Future[Seq[Entry]] = {
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ import components.AppComponent
|
|||
|
||||
@main
|
||||
def Fahrtenbuch(): Unit =
|
||||
val appComponent = AppComponent(Main.allEntries)
|
||||
val appComponent = AppComponent(Main.allEntries, Trystero.onlineStatus)
|
||||
|
||||
renderOnDomContentLoaded(
|
||||
dom.document.getElementById("app"),
|
||||
AppComponent(Main.allEntries).render()
|
||||
appComponent.render()
|
||||
)
|
||||
|
||||
object Main {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package fahrtenbuch
|
||||
|
||||
import com.raquo.laminar.api.L.*
|
||||
import org.scalajs.dom
|
||||
import org.scalajs.dom.RTCConfiguration
|
||||
import org.scalajs.dom.RTCIceServer
|
||||
import org.scalajs.dom.RTCPeerConnection
|
||||
|
|
@ -12,6 +13,10 @@ import typings.trystero.mod.joinRoom
|
|||
import typings.trystero.mod.selfId
|
||||
|
||||
import scala.scalajs.js
|
||||
import typings.trystero.mod.ActionProgress
|
||||
import typings.trystero.mod.ActionSender
|
||||
import typings.trystero.mod.ActionReceiver
|
||||
import model.Entry
|
||||
|
||||
object Trystero:
|
||||
private val eturn = new RTCIceServer:
|
||||
|
|
@ -35,11 +40,13 @@ object Trystero:
|
|||
}
|
||||
|
||||
// Public API
|
||||
val room: Room = joinRoom(MyConfig, "fahrtenbuch")
|
||||
val peerList: Var[List[(String, RTCPeerConnection)]] = Var(List.empty)
|
||||
val roomId = dom.window.location.hash
|
||||
val room: Room = joinRoom(MyConfig, roomId)
|
||||
println(s"joining room $roomId")
|
||||
val userId: Var[String] = Var(selfId)
|
||||
|
||||
// listen for incoming messages
|
||||
// track online peers
|
||||
val peerList: Var[List[(String, RTCPeerConnection)]] = Var(List.empty)
|
||||
def updatePeers(): Unit =
|
||||
peerList.set(room.getPeers().toList)
|
||||
println(s"my peer ID is $selfId")
|
||||
|
|
@ -51,3 +58,14 @@ object Trystero:
|
|||
println(s"$peerId left")
|
||||
updatePeers()
|
||||
)
|
||||
val onlineStatus: Signal[Boolean] = peerList.signal.map(_.nonEmpty)
|
||||
|
||||
object Actions:
|
||||
// setup actions
|
||||
private val entryAction: js.Tuple3[ActionSender[js.Any], ActionReceiver[
|
||||
js.Any
|
||||
], ActionProgress] = Trystero.room.makeAction[js.Any]("entry")
|
||||
private val trysteroReceiveEntry: ActionReceiver[js.Any] = entryAction._2
|
||||
|
||||
def sendEntry(entry: Entry): Unit =
|
||||
entryAction._1(entry.toNative)
|
||||
|
|
|
|||
7
src/main/scala/fahrtenbuch/Sync.scala
Normal file
7
src/main/scala/fahrtenbuch/Sync.scala
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package fahrtenbuch
|
||||
import com.raquo.laminar.api.L.*
|
||||
import fahrtenbuch.model.Entry
|
||||
|
||||
object Sync:
|
||||
val entrySyncOut =
|
||||
Observer[Entry](onNext = Actions.sendEntry(_))
|
||||
|
|
@ -1,17 +1,19 @@
|
|||
package fahrtenbuch.components
|
||||
|
||||
import com.raquo.laminar.api.L.*
|
||||
import fahrtenbuch.model.Entry
|
||||
import fahrtenbuch.model.{Entry, EntryId}
|
||||
import fahrtenbuch.Main.entryEditBus
|
||||
import rdts.base.Uid
|
||||
|
||||
class AppComponent(allEntries: Signal[Set[Entry]]):
|
||||
class AppComponent(
|
||||
allEntries: Signal[Set[Entry]],
|
||||
onlineStatus: Signal[Boolean]
|
||||
):
|
||||
// tracks whenever a user clicks on an edit button
|
||||
val editClickBus = new EventBus[(Uid, Boolean)]
|
||||
val editClickBus = new EventBus[(EntryId, Boolean)]
|
||||
|
||||
// tracks which entries are currently being edited
|
||||
val editStateSignal: Signal[Map[Uid, Boolean]] =
|
||||
editClickBus.stream.foldLeft(Map.empty[Uid, Boolean]) {
|
||||
val editStateSignal: Signal[Map[EntryId, Boolean]] =
|
||||
editClickBus.stream.foldLeft(Map.empty[EntryId, Boolean]) {
|
||||
case (acc, (id, value)) =>
|
||||
acc + (id -> value)
|
||||
}
|
||||
|
|
@ -37,7 +39,7 @@ class AppComponent(allEntries: Signal[Set[Entry]]):
|
|||
def render(): HtmlElement =
|
||||
div(
|
||||
cls := "app content",
|
||||
h1("Fahrtenbuch"),
|
||||
h1("Fahrtenbuch", OnlineStatusComponent(onlineStatus).render()),
|
||||
table(
|
||||
cls := "table",
|
||||
thead(
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
package fahrtenbuch.components
|
||||
import fahrtenbuch.model.Entry
|
||||
import fahrtenbuch.model.{Entry, EntryId}
|
||||
import com.raquo.laminar.nodes.ReactiveHtmlElement
|
||||
import org.scalajs.dom.HTMLTableRowElement
|
||||
import com.raquo.laminar.api.L.*
|
||||
import com.raquo.laminar.api.features.unitArrows
|
||||
import rdts.base.Uid
|
||||
import scala.util.Try
|
||||
|
||||
class EntryComponent(
|
||||
entry: Entry,
|
||||
editMode: Boolean,
|
||||
editClickBus: EventBus[(Uid, Boolean)],
|
||||
editClickBus: EventBus[(EntryId, Boolean)],
|
||||
entryEditBus: EventBus[Entry]
|
||||
):
|
||||
def render: ReactiveHtmlElement[HTMLTableRowElement] = {
|
||||
|
|
@ -93,11 +92,12 @@ class EntryComponent(
|
|||
editClickBus.emit(entry.id, false)
|
||||
entryEditBus.emit(
|
||||
entry.copy(
|
||||
startKm =
|
||||
entry.startKm.write(startKmInput.ref.value.toDouble),
|
||||
endKm = entry.endKm.write(endKmInput.ref.value.toDouble),
|
||||
animal = entry.animal.write(animalInput.ref.value),
|
||||
paid = entry.paid.write(paidCheckbox.ref.checked)
|
||||
driver = entry.driver.write(driverInput.ref.value)
|
||||
// startKm =
|
||||
// entry.startKm.write(startKmInput.ref.value.toDouble),
|
||||
// endKm = entry.endKm.write(endKmInput.ref.value.toDouble),
|
||||
// animal = entry.animal.write(animalInput.ref.value),
|
||||
// paid = entry.paid.write(paidCheckbox.ref.checked)
|
||||
)
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@ import com.raquo.laminar.api.L.*
|
|||
|
||||
import com.raquo.laminar.api.features.unitArrows
|
||||
import fahrtenbuch.Main.entryEditBus
|
||||
import fahrtenbuch.model.Entry
|
||||
import rdts.base.Uid
|
||||
import fahrtenbuch.model.{Entry, EntryId}
|
||||
import rdts.datatypes.LastWriterWins
|
||||
import scala.scalajs.js.Date
|
||||
import scala.util.Try
|
||||
|
|
@ -77,7 +76,7 @@ class NewEntryInput(showNewEntryField: Var[Boolean]):
|
|||
button(
|
||||
cls := "button is-success",
|
||||
onClick --> {
|
||||
val id = Uid.gen()
|
||||
val id = EntryId.gen()
|
||||
val driver = LastWriterWins.now(newEntryDriver.ref.value)
|
||||
val startKm = LastWriterWins.now(newEntryStartKm.ref.value.toDouble)
|
||||
val endKm = LastWriterWins.now(newEntryEndKm.ref.value.toDouble)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
package fahrtenbuch.components
|
||||
|
||||
import com.raquo.laminar.api.L.*
|
||||
import com.raquo.airstream.core.Signal
|
||||
|
||||
class OnlineStatusComponent(online: Signal[Boolean]):
|
||||
def render(): HtmlElement = {
|
||||
val status = online.map {
|
||||
case true => "Online"
|
||||
case false => "Offline"
|
||||
}
|
||||
|
||||
span(
|
||||
cls := "tag",
|
||||
text <-- status
|
||||
)
|
||||
}
|
||||
|
|
@ -6,9 +6,26 @@ import org.getshaka.nativeconverter.NativeConverter
|
|||
import org.getshaka.nativeconverter.ParseState
|
||||
import scala.scalajs.js
|
||||
import rdts.datatypes.LastWriterWins
|
||||
import rdts.base.Lattice
|
||||
|
||||
opaque type EntryId = Uid
|
||||
object EntryId:
|
||||
def gen(): EntryId = Uid.gen()
|
||||
def apply(id: String): EntryId = Uid.predefined(id)
|
||||
extension (id: EntryId) def delegate: String = id.delegate
|
||||
|
||||
given NativeConverter[EntryId] with {
|
||||
extension (a: EntryId)
|
||||
override def toNative: js.Any =
|
||||
a.delegate
|
||||
override def fromNative(ps: ParseState): Uid =
|
||||
Uid.predefined(ps.json.asInstanceOf[String])
|
||||
}
|
||||
|
||||
given Lattice[EntryId] = Lattice.assertEquals
|
||||
|
||||
case class Entry(
|
||||
id: Uid,
|
||||
id: EntryId,
|
||||
startKm: LastWriterWins[Double],
|
||||
endKm: LastWriterWins[Double],
|
||||
animal: LastWriterWins[String],
|
||||
|
|
@ -24,10 +41,4 @@ case class Entry(
|
|||
def costTotal: Double = costGas + costWear
|
||||
|
||||
object Entry:
|
||||
given NativeConverter[Uid] with {
|
||||
extension (a: Uid)
|
||||
override def toNative: js.Any =
|
||||
a.delegate
|
||||
override def fromNative(ps: ParseState): Uid =
|
||||
Uid.predefined(ps.json.asInstanceOf[String])
|
||||
}
|
||||
given Lattice[Entry] = Lattice.derived
|
||||
|
|
|
|||
Loading…
Reference in a new issue