Skip to content

Commit d204507

Browse files
committed
refactor: prefer typed key against strings
1 parent 025c5ec commit d204507

11 files changed

+91
-53
lines changed

frontend/src/main/scala/it/unibo/scafi/js/code/ExampleProvider.scala

+6-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ trait ExampleProvider {
1313
}
1414

1515
object ExampleProvider {
16-
private val storeKey = "examples"
16+
private val storeKey = new GlobalStore.Key {
17+
override type Data = String
18+
override val value: String = "examples"
19+
}
1720
private val examplesPath = s"${dom.window.location.href}config/examples.json"
1821
/** Combine provider by requesting the example in order. the first provider that returns a sequence of example
1922
* provider successfully complete the future
@@ -27,12 +30,12 @@ object ExampleProvider {
2730
override def getExamples: Future[Seq[ExampleGroup]] = Ajax
2831
.get(examplesPath)
2932
.map(_.responseText)
30-
.andThen { case Success(value) => GlobalStore.put(storeKey, value) }
33+
.andThen { case Success(value) => GlobalStore.put(storeKey)(value) }
3134
.map(result => read[Seq[ExampleGroup]](result))
3235
}
3336

3437
def fromGlobal()(implicit context: ExecutionContext): ExampleProvider = new ExampleProvider {
3538
override def getExamples: Future[Seq[ExampleGroup]] =
36-
Future.fromTry(GlobalStore.get[String](storeKey).map(data => read[Seq[ExampleGroup]](data)))
39+
Future.fromTry(GlobalStore.get(storeKey).map(data => read[Seq[ExampleGroup]](data)))
3740
}
3841
}

frontend/src/main/scala/it/unibo/scafi/js/controller/local/SupportConfiguration.scala

+6-2
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,16 @@ case class SupportConfiguration(
3636
coordinateMapping: CoordinateMapping = Identity
3737
)
3838
object SupportConfiguration {
39+
val key = new GlobalStore.Key {
40+
override type Data = String
41+
override val value: String = "configuration"
42+
}
3943
implicit def supportConfigurationRW: ReadWriter[SupportConfiguration] = macroRW[SupportConfiguration]
4044

41-
def storeGlobal(config: SupportConfiguration): Unit = GlobalStore.put("configuration", write(config))
45+
def storeGlobal(config: SupportConfiguration): Unit = GlobalStore.put(key)(write(config))
4246

4347
def loadGlobal(): Try[SupportConfiguration] =
44-
GlobalStore.get[String]("configuration").flatMap(elem => Try(read[SupportConfiguration](elem)))
48+
GlobalStore.get(key).flatMap(elem => Try(read[SupportConfiguration](elem)))
4549
}
4650

4751
/** top level trait to describe a network configuration that tells how node are placed in the world. */

frontend/src/main/scala/it/unibo/scafi/js/utils/GlobalStore.scala

+21-13
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,37 @@ import scala.scalajs.js
88
import scala.util.{Failure, Success, Try}
99

1010
object GlobalStore {
11+
/** Express any kind of Key accepted by the global store. It piggyback the data type stored inside the store. To
12+
* create a key, use anonymous class: val key = new GlobalStore.Key { type Data = Something val keyValue =
13+
* "something" }
14+
*/
15+
trait Key {
16+
type Data
17+
val value: String
18+
}
1119
private val bus = new EventBus()
12-
private case class GlobalData[A](name: String, value: A)
20+
private case class GlobalData[A](key: String, value: A)
1321
private val store = window.asInstanceOf[js.Dynamic] // a non safe way, think other possibilities
14-
def put(name: String, value: Any): Unit = {
15-
store.updateDynamic(name)(value.asInstanceOf[js.Any])
16-
bus.publish(GlobalData(name, value))
22+
def put(key: Key)(value: key.Data): Unit = {
23+
store.updateDynamic(key.value)(value.asInstanceOf[js.Any])
24+
bus.publish(GlobalData(key.value, value))
1725
}
18-
def listen[A](name: String)(on: A => Unit): CancelableFuture[Unit] = bus.listen { case GlobalData(`name`, value: A) =>
19-
on(value)
26+
def listen(key: Key)(on: key.Data => Unit): CancelableFuture[Unit] = bus.listen { case GlobalData(key.value, value) =>
27+
on(value.asInstanceOf[key.Data])
2028
}
21-
def get[A](name: String): Try[A] = {
22-
Try(store.selectDynamic(name).asInstanceOf[A]).flatMap {
29+
def get(key: Key): Try[key.Data] = {
30+
Try(store.selectDynamic(key.value).asInstanceOf[key.Data]).flatMap {
2331
case obj if js.isUndefined(obj) => Failure(new IllegalArgumentException("object not in global scope"))
2432
case other => Success(other)
2533
}
2634
}
27-
def getOrElse[A](name: String, orElse: A): A =
28-
get[A](name).getOrElse(orElse)
29-
def getOrElseUpdate[A](name: String, orElse: A): A = {
30-
get[A](name) match {
35+
def getOrElse(name: Key)(orElse: name.Data): name.Data =
36+
get(name).getOrElse(orElse)
37+
def getOrElseUpdate(name: Key)(orElse: name.Data): name.Data = {
38+
get(name) match {
3139
case Success(value) => value
3240
case Failure(_) =>
33-
put(name, orElse)
41+
put(name)(orElse)
3442
orElse
3543
}
3644
}

frontend/src/main/scala/it/unibo/scafi/js/view/dynamic/EditorSection.scala

+12-5
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,14 @@ trait EditorSection {
2323
}
2424

2525
object EditorSection {
26-
26+
val modeKey = new GlobalStore.Key {
27+
type Data = Mode
28+
override val value: String = "mode"
29+
}
30+
val docKey = new GlobalStore.Key {
31+
override type Data = Doc
32+
override val value: String = "doc"
33+
}
2734
trait Mode extends js.Object {
2835
def lang: String
2936

@@ -77,7 +84,7 @@ object EditorSection {
7784
}
7885

7986
private class EditorSectionImpl(editorZone: Div, defaultMode: Mode = ScalaModeEasy) extends EditorSection {
80-
var mode: Mode = GlobalStore.get[Mode]("mode") match {
87+
var mode: Mode = GlobalStore.get(EditorSection.modeKey) match {
8188
case Failure(exception) => defaultMode
8289
case Success(mode) => mode
8390
}
@@ -101,7 +108,7 @@ object EditorSection {
101108
modeSelection.on()
102109
}
103110
)
104-
val editor = GlobalStore.get[Doc]("doc") match {
111+
val editor = GlobalStore.get(docKey) match {
105112
case Success(doc) =>
106113
val config = new EditorConfiguration(doc.getValue(), mode.codeMirrorMode, "native", true, "material")
107114
val editor = CodeMirror(editorZone, config)
@@ -114,7 +121,7 @@ object EditorSection {
114121
ThemeSwitcher.onDark(editor.setOption("theme", "material"))
115122
ThemeSwitcher.onLight(editor.setOption("theme", "default"))
116123

117-
GlobalStore.put("doc", editor.doc)
124+
GlobalStore.put(docKey)(editor.doc)
118125
modeSelection.onClick = () => {
119126
if (mode.lang == ScalaModeEasy.lang) {
120127
val fullCode = ScalaModeEasy.convertToFull(this.getRaw())
@@ -132,7 +139,7 @@ object EditorSection {
132139
case _ =>
133140
}
134141
editor.setValue(code)
135-
GlobalStore.put("mode", mode)
142+
GlobalStore.put(modeKey)(mode)
136143
this.mode = mode
137144
editor.setOption("mode", mode.codeMirrorMode)
138145
}

frontend/src/main/scala/it/unibo/scafi/js/view/dynamic/SimulationControlsSection.scala

+7-3
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class SimulationControlsSection {
5353
simulation = Some(daemon)
5454
stopButton.disabled = false
5555
(tick :: startButton :: Nil) foreach { el => el.disabled = true }
56+
case _ =>
5657
}
5758

5859
tick.onclick = _ =>
@@ -115,8 +116,11 @@ class SimulationControlsSection {
115116
}
116117

117118
private case class VelocitySelector(slow: Int, normal: Int, fast: Int) extends HtmlRenderable[Element] {
118-
private val globalLabel = "velocitySelector"
119-
val active: String = GlobalStore.getOrElseUpdate(globalLabel, "slow")
119+
private val key = new GlobalStore.Key {
120+
override type Data = String
121+
override val value: String = "velocitySelector"
122+
}
123+
val active: String = GlobalStore.getOrElseUpdate(key)("slow")
120124
var onChangeRadio: () => Unit = () => {}
121125
var batchSize: Int = active match {
122126
case "slow" => slow
@@ -164,7 +168,7 @@ class SimulationControlsSection {
164168
"change",
165169
() => {
166170
batchSize = value
167-
GlobalStore.put(globalLabel, name)
171+
GlobalStore.put(key)(name)
168172
onChangeRadio()
169173
}
170174
)

frontend/src/main/scala/it/unibo/scafi/js/view/dynamic/ThemeSwitcher.scala

+8-5
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,22 @@ import scalatags.JsDom.all
1010
import scala.scalajs.js
1111

1212
object ThemeSwitcher {
13-
1413
import all._
1514

15+
private val keyTheme = new GlobalStore.Key {
16+
override type Data = Theme
17+
override val value: String = "theme"
18+
}
1619
private val lightStyle = link(id := "light", rel := "stylesheet", href := "style/light.css").render
1720

18-
private var theme: Theme = GlobalStore.get[Theme]("theme").getOrElse(Dark)
21+
private var theme: Theme = GlobalStore.get(keyTheme).getOrElse(Dark)
1922

20-
def onLight(action: => Unit): CancelableFuture[Unit] = GlobalStore.listen[Theme]("theme") {
23+
def onLight(action: => Unit): CancelableFuture[Unit] = GlobalStore.listen(keyTheme) {
2124
case theme if theme.value == Light.value => action
2225
case _ =>
2326
}
2427

25-
def onDark(action: => Unit): CancelableFuture[Unit] = GlobalStore.listen[Theme]("theme") {
28+
def onDark(action: => Unit): CancelableFuture[Unit] = GlobalStore.listen(keyTheme) {
2629
case theme if theme.value == Dark.value => action
2730
case _ =>
2831
}
@@ -43,7 +46,7 @@ object ThemeSwitcher {
4346
}
4447

4548
private def install(theme: Theme): Unit = {
46-
GlobalStore.put("theme", theme)
49+
GlobalStore.put(keyTheme)(theme)
4750
theme.value match {
4851
case Light.value if !exist(lightStyle.id) => document.head.appendChild(lightStyle)
4952
case Dark.value if exist(lightStyle.id) => $(s"#${lightStyle.id}").remove()

frontend/src/main/scala/it/unibo/scafi/js/view/dynamic/VisualizationSettingsSection.scala

+12-11
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@ object VisualizationSettingsSection {
3030
override val sensorsMenu: SensorsMenu,
3131
visualizationDropMenu: Div
3232
) extends VisualizationSettingsSection {
33-
private val labelsEnabledGlobal = "labelEnabled"
34-
private val labels = GlobalStore.getOrElseUpdate(
35-
labelsEnabledGlobal,
33+
private val labelsEnabledKey = new GlobalStore.Key {
34+
type Data = js.Dictionary[Boolean]
35+
override val value = "labelEnabled"
36+
}
37+
private val labels = GlobalStore.getOrElseUpdate(labelsEnabledKey)(
3638
js.Dictionary(
3739
"id" -> true,
3840
"neighborhood" -> true,
@@ -52,8 +54,7 @@ object VisualizationSettingsSection {
5254
private val minNodeSize = 4
5355
private val maxNodeSize = 20
5456
private val standardNodeSize = 7
55-
val initialConfig = GlobalStore.getOrElseUpdate(
56-
VisualizationSetting.globalName,
57+
private val initialConfig = GlobalStore.getOrElseUpdate(VisualizationSetting.key)(
5758
VisualizationSetting(standardFontSize, standardNodeSize)
5859
)
5960
private val fontSizeTag = NumericVizInput(
@@ -62,8 +63,8 @@ object VisualizationSettingsSection {
6263
maxFontSize,
6364
initialConfig.fontSize,
6465
font => {
65-
val config = GlobalStore.get[VisualizationSetting](VisualizationSetting.globalName).get
66-
GlobalStore.put(VisualizationSetting.globalName, config.changeFont(font))
66+
val config = GlobalStore.get(VisualizationSetting.key).get
67+
GlobalStore.put(VisualizationSetting.key)(config.changeFont(font))
6768
}
6869
)
6970
private val nodeSizeTag = NumericVizInput(
@@ -72,8 +73,8 @@ object VisualizationSettingsSection {
7273
maxNodeSize,
7374
initialConfig.nodeSize,
7475
node => {
75-
val config = GlobalStore.get[VisualizationSetting](VisualizationSetting.globalName).get
76-
GlobalStore.put(VisualizationSetting.globalName, config.changeNode(node))
76+
val config = GlobalStore.get(VisualizationSetting.key).get
77+
GlobalStore.put(VisualizationSetting.key)(config.changeNode(node))
7778
}
7879
)
7980

@@ -99,9 +100,9 @@ object VisualizationSettingsSection {
99100
settingDiv.appendChild(sensorsMenu.html)
100101

101102
private def onLabelChange(e: MouseEvent, name: String): Unit = {
102-
val labelsMap = GlobalStore.get[js.Dictionary[Boolean]](labelsEnabledGlobal).get
103+
val labelsMap = GlobalStore.get(labelsEnabledKey).get
103104
labelsMap.put(name, !labelsMap(name))
104-
GlobalStore.put(labelsEnabledGlobal, labelsMap)
105+
GlobalStore.put(labelsEnabledKey)(labelsMap)
105106
Toggle.Repaint(e)
106107
}
107108
}

frontend/src/main/scala/it/unibo/scafi/js/view/dynamic/graph/LabelRender.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ object LabelRender {
7070
world: Graph,
7171
scene: Scene
7272
): Output = {
73-
val (fontSize, width) = GlobalStore.get[VisualizationSetting](VisualizationSetting.globalName) match {
73+
val (fontSize, width) = GlobalStore.get(VisualizationSetting.key) match {
7474
case Success(VisualizationSetting(fontSize, nodeWidth)) => (fontSize, nodeWidth: JSNumber)
7575
case Failure(_) => (fallBackSize, node.width)
7676
}
@@ -182,7 +182,7 @@ object LabelRender {
182182
world: Graph,
183183
scene: Scene
184184
): Output = {
185-
val fullMatrix = GlobalStore.get[VisualizationSetting](VisualizationSetting.globalName) match {
185+
val fullMatrix = GlobalStore.get(VisualizationSetting.key) match {
186186
case Success(VisualizationSetting(_, nodeSize)) => (nodeSize: Double)
187187
case Failure(_) => fallBackSize
188188
}

frontend/src/main/scala/it/unibo/scafi/js/view/dynamic/graph/PhaserGraphSection.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class PhaserGraphSection(
5757
private lazy val sceneHandler: types.scenes.CreateSceneFromObjectConfig = types.scenes.callbacks(
5858
preload = scene => labelRenders.foreach(_.onInit(scene)),
5959
create = (scene, _) => {
60-
GlobalStore.listen[Any](VisualizationSetting.globalName)(_ => PageBus.publish(ForceRepaint))
60+
GlobalStore.listen(VisualizationSetting.key)(_ => PageBus.publish(ForceRepaint))
6161
val mainCamera = scene.cameras.main
6262
mainCamera.zoom = 1
6363
Debug("scene", scene)

frontend/src/main/scala/it/unibo/scafi/js/view/static/PageStructure.scala

+10-7
Original file line numberDiff line numberDiff line change
@@ -44,27 +44,27 @@ object PageStructure {
4444
private val doubleClickEvent = (ev: js.Any) => {
4545
val backendSection = $("#backend-config-section")
4646
split.destroy()
47-
val divisions = GlobalStore.get[PageDivision](sizesInGlobal).get
47+
val divisions = GlobalStore.get(sizesKey).get
4848
if (divisions.collapsed) {
4949
divisions.elems(config) = configSection
5050
divisions.elems(visualization) =
5151
new SplitSection(divisions.elems(visualization).size - configSection.size, minVisualizationPortion)
5252
backendSection.show()
53-
GlobalStore.put(sizesInGlobal, new PageDivision(false, divisions.elems))
53+
GlobalStore.put(sizesKey)(new PageDivision(false, divisions.elems))
5454
split = createSplit(gutterCreator, sections, divisions.elems: _*)
5555
} else {
5656
backendSection.hide()
5757
val oldConfig = divisions.elems(config)
5858
divisions.elems(config) = empty
5959
divisions.elems(visualization) =
6060
new SplitSection(divisions.elems(visualization).size + oldConfig.size, minVisualizationPortion)
61-
GlobalStore.put(sizesInGlobal, new PageDivision(true, divisions.elems))
61+
GlobalStore.put(sizesKey)(new PageDivision(true, divisions.elems))
6262
split = createSplit(gutterCreator, sections, divisions.elems: _*)
6363
}
6464
}
6565

6666
def install(): Unit = {
67-
val divisions: PageDivision = GlobalStore.getOrElseUpdate(sizesInGlobal, standardConfig)
67+
val divisions: PageDivision = GlobalStore.getOrElseUpdate(sizesKey)(standardConfig)
6868
val backendSection = $("#backend-config-section")
6969
if (divisions.collapsed) {
7070
backendSection.hide()
@@ -89,7 +89,7 @@ object PageStructure {
8989
"expandToMin" -> true,
9090
"onDrag" -> ((elems: js.Array[Double]) => {
9191
split.destroy()
92-
val oldConfig = GlobalStore.get[PageDivision](sizesInGlobal).get
92+
val oldConfig = GlobalStore.get(sizesKey).get
9393
val newPageDivision = new PageDivision(
9494
oldConfig.collapsed,
9595
js.Array(
@@ -105,7 +105,7 @@ object PageStructure {
105105
)
106106
)
107107
install()
108-
GlobalStore.put(sizesInGlobal, newPageDivision)
108+
GlobalStore.put(sizesKey)(newPageDivision)
109109
})
110110
)
111111
)
@@ -114,5 +114,8 @@ object PageStructure {
114114

115115
class SplitSection(val size: Double, val minSize: Int) extends js.Object
116116
class PageDivision(val collapsed: Boolean, val elems: js.Array[SplitSection]) extends js.Object
117-
val sizesInGlobal = "page-sizes"
117+
val sizesKey = new GlobalStore.Key {
118+
type Data = PageDivision
119+
override val value = "page-sizes"
120+
}
118121
}

frontend/src/main/scala/it/unibo/scafi/js/view/static/VisualizationSetting.scala

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package it.unibo.scafi.js.view.static
22

3+
import it.unibo.scafi.js.utils.GlobalStore
4+
35
import scala.scalajs.js
46

57
class VisualizationSetting(val fontSize: Int, val nodeSize: Int) extends js.Object {
@@ -8,7 +10,10 @@ class VisualizationSetting(val fontSize: Int, val nodeSize: Int) extends js.Obje
810
val vizPatternMatch: Unit = {}
911
}
1012
object VisualizationSetting {
11-
val globalName = "visualization-setting"
13+
val key = new GlobalStore.Key {
14+
type Data = VisualizationSetting
15+
override val value = "visualization-setting"
16+
}
1217
def apply(fontSize: Int, nodeSize: Int): VisualizationSetting = new VisualizationSetting(fontSize, nodeSize)
1318
def unapply(value: js.Object): Option[(Int, Int)] = if (value.hasOwnProperty("vizPatternMatch")) {
1419
val res = value.asInstanceOf[VisualizationSetting]

0 commit comments

Comments
 (0)