Skip to content

Commit

Permalink
feat(ui,signatures): add paste shortcut to paste signatures directly …
Browse files Browse the repository at this point in the history
…when a system is selected
  • Loading branch information
updraft0 committed Feb 15, 2025
1 parent 34147cf commit 0f9f7fb
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 7 deletions.
2 changes: 1 addition & 1 deletion ui/src/main/scala/controltower/backend/client.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import sttp.tapir.client.sttp.{SttpClientInterpreter, WebSocketToPipe}

import scala.concurrent.Future

class ControlTowerBackend(
final class ControlTowerBackend(
// no default hardcoding of backend urls - rely on frontend proxy to route appropriately
val backendUrlOpt: Option[Uri] = None,
val wsUrlOpt: Option[Uri] = None
Expand Down
51 changes: 45 additions & 6 deletions ui/src/main/scala/controltower/page/map/view/MapView.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import controltower.db.ReferenceDataStore
import controltower.page.map.*
import controltower.ui.*
import io.laminext.websocket.*
import org.scalajs.dom
import org.scalajs.dom.KeyboardEvent
import org.updraft0.controltower.constant.SystemId
import org.updraft0.controltower.protocol.*
Expand Down Expand Up @@ -178,7 +179,7 @@ private final class MapView(
contextMenuView.view,
// A -> add system
modalKeyBinding(
"KeyA",
"a",
controller.mapRole.map(RoleController.canAddSystem).combineWith(ws.isConnected).map(_ && _),
_.map(ev => (ev, ())),
(_, onClose) =>
Expand Down Expand Up @@ -208,7 +209,7 @@ private final class MapView(
),
// P -> paste system signatures
modalKeyBinding(
"KeyP",
"p",
controller.mapRole.map(RoleController.canEditSignatures).combineWith(ws.isConnected).map(_ && _),
_.filterWith(controller.selectedSystem, _.isDefined)
.withCurrentValueOf(controller.selectedSystem)
Expand All @@ -231,9 +232,24 @@ private final class MapView(
)
}
),
// paste system signatures without confirmation // TODO why is event handler only fired on document level
clipboardEventBinding[(SystemId, Instant)](
documentEvents(_.onPaste),
controller.mapRole.map(RoleController.canEditSignatures).combineWith(ws.isConnected).map(_ && _),
_.filterWith(controller.selectedSystemId, _.isDefined)
.withCurrentValueOf(controller.selectedSystemId, time)
.map((ev, opt, now) => (ev, (opt.get, now))),
{ case (clipboard, (systemId, now), onClose) =>
parseLines(clipboard)
.flatMap(_.map(parseLineToSignature(_, now)).sequence)
.foreach: sigs =>
controller.actionsBus.onNext(MapAction.UpdateSignatures(systemId, false, sigs.toArray))
onClose.onNext(())
}
),
// R -> rename system
modalKeyBinding(
"KeyR",
"r",
controller.mapRole.map(RoleController.canRenameSystem).combineWith(ws.isConnected).map(_ && _),
_.filterWith(controller.selectedSystem, _.isDefined)
.withCurrentValueOf(controller.selectedSystem)
Expand Down Expand Up @@ -332,14 +348,22 @@ private final class MapView(
)

private def modalKeyBinding[B](
code: String,
key: String,
shouldEnable: Signal[Boolean],
compose: EventStream[KeyboardEvent] => EventStream[(KeyboardEvent, B)],
action: (B, Observer[Unit]) => Unit
action: (B, Observer[Unit]) => Unit,
ctrlOn: Boolean = false,
shiftOn: Boolean = false,
metaOn: Boolean = false
) =
documentEvents(
_.onKeyDown
.filter(ev => !ev.repeat && ev.code == code && !ev.ctrlKey && !ev.shiftKey && !ev.metaKey)
.filter(ev =>
!ev.repeat && ev.key == key &&
((ctrlOn && ev.ctrlKey) || (!ctrlOn && !ev.ctrlKey)) &&
((shiftOn && ev.shiftKey) || (!shiftOn && !ev.shiftKey)) &&
((metaOn && ev.metaKey) || (!metaOn && !ev.metaKey))
)
).compose(es =>
compose(es).filterWith(Modal.Shown.signal.map(!_)).filterWith(shouldEnable).filterWith(notInKeyHandler.signal)
) --> { (ev, b) =>
Expand All @@ -348,6 +372,21 @@ private final class MapView(
action(b, Observer(_ => inKeyHandler.set(false)))
}

private def clipboardEventBinding[B](
event: EventStream[dom.ClipboardEvent],
shouldEnable: Signal[Boolean],
compose: EventStream[dom.ClipboardEvent] => EventStream[(dom.ClipboardEvent, B)],
action: (String, B, Observer[Unit]) => Unit
) =
event.compose(es =>
compose(es).filterWith(Modal.Shown.signal.map(!_)).filterWith(shouldEnable).filterWith(notInKeyHandler.signal)
) --> { (ev, b) =>
ev.preventDefault()
inKeyHandler.set(true)
val s = ev.clipboardData.getData("text")
action(s, b, Observer(_ => inKeyHandler.set(false)))
}

object MapView:
import org.updraft0.controltower.protocol.jsoncodec.given
import sttp.client3.UriContext
Expand Down

0 comments on commit 0f9f7fb

Please # to comment.