diff --git a/frontend/react/src/audio/audiomanager.ts b/frontend/react/src/audio/audiomanager.ts
index c6a49212..ac679f3e 100644
--- a/frontend/react/src/audio/audiomanager.ts
+++ b/frontend/react/src/audio/audiomanager.ts
@@ -11,6 +11,7 @@ import { AudioSink } from "./audiosink";
import { Unit } from "../unit/unit";
import { UnitSink } from "./unitsink";
import { AudioPacket, MessageType } from "./audiopacket";
+import { AudioManagerStateChangedEvent, AudioSinksChangedEvent, AudioSourcesChangedEvent, ConfigLoadedEvent, SRSClientsChangedEvent } from "../events";
export class AudioManager {
#audioContext: AudioContext;
@@ -35,7 +36,7 @@ export class AudioManager {
#SRSClientUnitIDs: number[] = [];
constructor() {
- document.addEventListener("configLoaded", () => {
+ ConfigLoadedEvent.on(() => {
let config = getApp().getConfig();
if (config["WSPort"]) {
this.setPort(config["WSPort"]);
@@ -95,7 +96,7 @@ export class AudioManager {
});
} else {
this.#SRSClientUnitIDs = JSON.parse(new TextDecoder().decode(packetUint8Array.slice(1))).unitIDs;
- document.dispatchEvent(new CustomEvent("SRSClientsUpdated"));
+ SRSClientsChangedEvent.dispatch();
}
}
});
@@ -108,13 +109,13 @@ export class AudioManager {
if (sink instanceof RadioSink) microphoneSource.connect(sink);
});
this.#sources.push(microphoneSource);
- document.dispatchEvent(new CustomEvent("audioSourcesUpdated"));
+ AudioSourcesChangedEvent.dispatch(getApp().getAudioManager().getSources());
/* Add two default radios */
this.addRadio();
this.addRadio();
});
- document.dispatchEvent(new CustomEvent("audioManagerStateChanged"));
+ AudioManagerStateChangedEvent.dispatch(this.#running);
}
stop() {
@@ -128,9 +129,9 @@ export class AudioManager {
this.#sources = [];
this.#sinks = [];
- document.dispatchEvent(new CustomEvent("audioSourcesUpdated"));
- document.dispatchEvent(new CustomEvent("audioSinksUpdated"));
- document.dispatchEvent(new CustomEvent("audioManagerStateChanged"));
+ AudioSourcesChangedEvent.dispatch(this.#sources);
+ AudioSinksChangedEvent.dispatch(this.#sinks);
+ AudioManagerStateChangedEvent.dispatch(this.#running);
}
setAddress(address) {
@@ -153,7 +154,7 @@ export class AudioManager {
}
const newSource = new FileSource(file);
this.#sources.push(newSource);
- document.dispatchEvent(new CustomEvent("audioSourcesUpdated"));
+ AudioSourcesChangedEvent.dispatch(getApp().getAudioManager().getSources());
}
getSources() {
@@ -168,7 +169,7 @@ export class AudioManager {
}
source.disconnect();
this.#sources = this.#sources.filter((v) => v != source);
- document.dispatchEvent(new CustomEvent("audioSourcesUpdated"));
+ AudioSourcesChangedEvent.dispatch(this.#sources);
}
addUnitSink(unit: Unit) {
@@ -178,7 +179,7 @@ export class AudioManager {
return;
}
this.#sinks.push(new UnitSink(unit));
- document.dispatchEvent(new CustomEvent("audioSinksUpdated"));
+ AudioSinksChangedEvent.dispatch(this.#sinks);
}
addRadio() {
@@ -191,7 +192,7 @@ export class AudioManager {
this.#sinks.push(newRadio);
newRadio.setName(`Radio ${this.#sinks.length}`);
this.#sources[0].connect(newRadio);
- document.dispatchEvent(new CustomEvent("audioSinksUpdated"));
+ AudioSinksChangedEvent.dispatch(this.#sinks);
}
getSinks() {
@@ -210,7 +211,7 @@ export class AudioManager {
this.#sinks.forEach((sink) => {
if (sink instanceof RadioSink) sink.setName(`Radio ${idx++}`);
});
- document.dispatchEvent(new CustomEvent("audioSinksUpdated"));
+ AudioSinksChangedEvent.dispatch(getApp().getAudioManager().getSinks());
this.#sources.forEach((source) => {
if (source.getConnectedTo().includes(sink))
source.disconnect(sink)
diff --git a/frontend/react/src/audio/audiosink.ts b/frontend/react/src/audio/audiosink.ts
index 5aea5edb..f9949796 100644
--- a/frontend/react/src/audio/audiosink.ts
+++ b/frontend/react/src/audio/audiosink.ts
@@ -1,3 +1,4 @@
+import { AudioSinksChangedEvent } from "../events";
import { getApp } from "../olympusapp";
/* Base audio sink class */
@@ -19,7 +20,7 @@ export abstract class AudioSink {
disconnect() {
this.getInputNode().disconnect();
- document.dispatchEvent(new CustomEvent("audioSinksUpdated"));
+ AudioSinksChangedEvent.dispatch(getApp().getAudioManager().getSinks());
}
getInputNode() {
diff --git a/frontend/react/src/audio/audiosource.ts b/frontend/react/src/audio/audiosource.ts
index 7072cbe8..175832ff 100644
--- a/frontend/react/src/audio/audiosource.ts
+++ b/frontend/react/src/audio/audiosource.ts
@@ -1,3 +1,4 @@
+import { AudioSourcesChangedEvent } from "../events";
import { getApp } from "../olympusapp";
import { AudioSink } from "./audiosink";
import { WebAudioPeakMeter } from "web-audio-peak-meter";
@@ -21,7 +22,7 @@ export abstract class AudioSource {
if (!this.#connectedTo.includes(sink)) {
this.getOutputNode().connect(sink.getInputNode());
this.#connectedTo.push(sink);
- document.dispatchEvent(new CustomEvent("audioSourcesUpdated"));
+ AudioSourcesChangedEvent.dispatch(getApp().getAudioManager().getSources());
}
}
@@ -33,7 +34,7 @@ export abstract class AudioSource {
this.getOutputNode().disconnect();
}
- document.dispatchEvent(new CustomEvent("audioSourcesUpdated"));
+ AudioSourcesChangedEvent.dispatch(getApp().getAudioManager().getSources());
}
setName(name) {
@@ -51,7 +52,7 @@ export abstract class AudioSource {
setVolume(volume) {
this.#volume = volume;
this.#gainNode.gain.exponentialRampToValueAtTime(volume, getApp().getAudioManager().getAudioContext().currentTime + 0.02);
- document.dispatchEvent(new CustomEvent("audioSourcesUpdated"));
+ AudioSourcesChangedEvent.dispatch(getApp().getAudioManager().getSources());
}
getVolume() {
diff --git a/frontend/react/src/audio/filesource.ts b/frontend/react/src/audio/filesource.ts
index 647c134a..29647c6b 100644
--- a/frontend/react/src/audio/filesource.ts
+++ b/frontend/react/src/audio/filesource.ts
@@ -1,5 +1,6 @@
import { AudioSource } from "./audiosource";
import { getApp } from "../olympusapp";
+import { AudioSourcesChangedEvent } from "../events";
export class FileSource extends AudioSource {
#file: File;
@@ -50,7 +51,7 @@ export class FileSource extends AudioSource {
const now = Date.now() / 1000;
this.#lastUpdateTime = now;
- document.dispatchEvent(new CustomEvent("audioSourcesUpdated"));
+ AudioSourcesChangedEvent.dispatch(getApp().getAudioManager().getSources());
this.#updateInterval = setInterval(() => {
/* Update the current position value every second */
@@ -63,7 +64,7 @@ export class FileSource extends AudioSource {
if (!this.#looping) this.pause();
}
- document.dispatchEvent(new CustomEvent("audioSourcesUpdated"));
+ AudioSourcesChangedEvent.dispatch(getApp().getAudioManager().getSources());
}, 1000);
}
@@ -77,7 +78,7 @@ export class FileSource extends AudioSource {
this.#currentPosition += now - this.#lastUpdateTime;
clearInterval(this.#updateInterval);
- document.dispatchEvent(new CustomEvent("audioSourcesUpdated"));
+ AudioSourcesChangedEvent.dispatch(getApp().getAudioManager().getSources());
}
getPlaying() {
@@ -110,7 +111,7 @@ export class FileSource extends AudioSource {
setLooping(looping) {
this.#looping = looping;
if (this.#source) this.#source.loop = looping;
- document.dispatchEvent(new CustomEvent("audioSourcesUpdated"));
+ AudioSourcesChangedEvent.dispatch(getApp().getAudioManager().getSources());
}
getLooping() {
diff --git a/frontend/react/src/audio/microphonesource.ts b/frontend/react/src/audio/microphonesource.ts
index f744c804..17c584e2 100644
--- a/frontend/react/src/audio/microphonesource.ts
+++ b/frontend/react/src/audio/microphonesource.ts
@@ -1,3 +1,4 @@
+import { AudioSourcesChangedEvent } from "../events";
import { getApp } from "../olympusapp";
import { AudioSource } from "./audiosource";
@@ -20,6 +21,6 @@ export class MicrophoneSource extends AudioSource {
}
play() {
- document.dispatchEvent(new CustomEvent("audioSourcesUpdated"));
+ AudioSourcesChangedEvent.dispatch(getApp().getAudioManager().getSources());
}
}
diff --git a/frontend/react/src/audio/radiosink.ts b/frontend/react/src/audio/radiosink.ts
index f6767b4e..fa813e49 100644
--- a/frontend/react/src/audio/radiosink.ts
+++ b/frontend/react/src/audio/radiosink.ts
@@ -1,6 +1,7 @@
import { AudioSink } from "./audiosink";
import { AudioPacket } from "./audiopacket";
import { getApp } from "../olympusapp";
+import { AudioSinksChangedEvent } from "../events";
let packetID = 0;
@@ -54,7 +55,7 @@ export class RadioSink extends AudioSink {
setFrequency(frequency) {
this.#frequency = frequency;
- document.dispatchEvent(new CustomEvent("audioSinksUpdated"));
+ AudioSinksChangedEvent.dispatch(getApp().getAudioManager().getSinks());
}
getFrequency() {
@@ -63,7 +64,7 @@ export class RadioSink extends AudioSink {
setModulation(modulation) {
this.#modulation = modulation;
- document.dispatchEvent(new CustomEvent("audioSinksUpdated"));
+ AudioSinksChangedEvent.dispatch(getApp().getAudioManager().getSinks());
}
getModulation() {
@@ -72,7 +73,7 @@ export class RadioSink extends AudioSink {
setPtt(ptt) {
this.#ptt = ptt;
- document.dispatchEvent(new CustomEvent("audioSinksUpdated"));
+ AudioSinksChangedEvent.dispatch(getApp().getAudioManager().getSinks());
}
getPtt() {
@@ -81,7 +82,7 @@ export class RadioSink extends AudioSink {
setTuned(tuned) {
this.#tuned = tuned;
- document.dispatchEvent(new CustomEvent("audioSinksUpdated"));
+ AudioSinksChangedEvent.dispatch(getApp().getAudioManager().getSinks());
}
getTuned() {
@@ -90,7 +91,7 @@ export class RadioSink extends AudioSink {
setVolume(volume) {
this.#volume = volume;
- document.dispatchEvent(new CustomEvent("audioSinksUpdated"));
+ AudioSinksChangedEvent.dispatch(getApp().getAudioManager().getSinks());
}
getVolume() {
diff --git a/frontend/react/src/audio/unitsink.ts b/frontend/react/src/audio/unitsink.ts
index 73861c70..087a56ae 100644
--- a/frontend/react/src/audio/unitsink.ts
+++ b/frontend/react/src/audio/unitsink.ts
@@ -2,6 +2,7 @@ import { AudioSink } from "./audiosink";
import { getApp } from "../olympusapp";
import { Unit } from "../unit/unit";
import { AudioUnitPipeline } from "./audiounitpipeline";
+import { AudioSinksChangedEvent, SRSClientsChangedEvent } from "../events";
/* Unit sink to implement a "loudspeaker" external sound. Useful for stuff like 5MC calls, air sirens,
scramble calls and so on. Ideally, one may want to move this code to the backend*/
@@ -17,7 +18,7 @@ export class UnitSink extends AudioSink {
this.#unit = unit;
this.setName(`${unit.getUnitName()} - ${unit.getName()}`);
- document.addEventListener("SRSClientsUpdated", () => {
+ SRSClientsChangedEvent.on(() => {
this.#updatePipelines();
});
@@ -53,7 +54,7 @@ export class UnitSink extends AudioSink {
Object.values(this.#unitPipelines).forEach((pipeline) => {
pipeline.setPtt(ptt);
})
- document.dispatchEvent(new CustomEvent("audioSinksUpdated"));
+ AudioSinksChangedEvent.dispatch(getApp().getAudioManager().getSinks());
}
getPtt() {
@@ -65,7 +66,7 @@ export class UnitSink extends AudioSink {
Object.values(this.#unitPipelines).forEach((pipeline) => {
pipeline.setMaxDistance(maxDistance);
})
- document.dispatchEvent(new CustomEvent("audioSinksUpdated"));
+ AudioSinksChangedEvent.dispatch(getApp().getAudioManager().getSinks());
}
getMaxDistance() {
diff --git a/frontend/react/src/events.ts b/frontend/react/src/events.ts
new file mode 100644
index 00000000..f5409271
--- /dev/null
+++ b/frontend/react/src/events.ts
@@ -0,0 +1,280 @@
+import { AudioSink } from "./audio/audiosink";
+import { AudioSource } from "./audio/audiosource";
+import { OlympusState, OlympusSubState } from "./constants/constants";
+import { ServerStatus } from "./interfaces";
+import { CoalitionCircle } from "./map/coalitionarea/coalitioncircle";
+import { CoalitionPolygon } from "./map/coalitionarea/coalitionpolygon";
+import { Airbase } from "./mission/airbase";
+import { MapHiddenTypes, MapOptions } from "./types/types";
+import { ContextAction } from "./unit/contextaction";
+import { ContextActionSet } from "./unit/contextactionset";
+import { Unit } from "./unit/unit";
+
+export class BaseOlympusEvent {
+ static on(callback: () => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback();
+ });
+ }
+
+ static dispatch() {
+ document.dispatchEvent(new CustomEvent(this.name));
+ console.log(`Event ${this.name} dispatched`);
+ }
+}
+
+export class BaseUnitEvent {
+ static on(callback: (unit: Unit) => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback(ev.detail.unit);
+ });
+ }
+
+ static dispatch(unit: Unit) {
+ document.dispatchEvent(new CustomEvent(this.name, { detail: { unit } }));
+ console.log(`Event ${this.name} dispatched`);
+ console.log(unit)
+ }
+}
+
+/************** App events ***************/
+export class AppStateChangedEvent {
+ static on(callback: (state: OlympusState, subState: OlympusSubState) => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback(ev.detail.state, ev.detail.subState);
+ });
+ }
+
+ static dispatch(state: OlympusState, subState: OlympusSubState) {
+ const detail = { state, subState };
+ document.dispatchEvent(new CustomEvent(this.name, { detail }));
+ console.log(`Event ${this.name} dispatched with detail:`);
+ console.log(detail);
+ }
+}
+
+export class ConfigLoadedEvent {
+ /* TODO add config */
+ static on(callback: () => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback();
+ });
+ }
+
+ static dispatch() {
+ document.dispatchEvent(new CustomEvent(this.name));
+ console.log(`Event ${this.name} dispatched`);
+ }
+}
+
+export class ServerStatusUpdatedEvent {
+ static on(callback: (serverStatus: ServerStatus) => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback(ev.detail.serverStatus);
+ });
+ }
+
+ static dispatch(serverStatus: ServerStatus) {
+ document.dispatchEvent(new CustomEvent(this.name, { detail: { serverStatus } }));
+ // Logging disabled since periodic
+ }
+}
+
+/************** Map events ***************/
+export class HiddenTypesChangedEvent {
+ static on(callback: (hiddenTypes: MapHiddenTypes) => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback(ev.detail.hiddenTypes);
+ });
+ }
+
+ static dispatch(hiddenTypes: MapHiddenTypes) {
+ document.dispatchEvent(new CustomEvent(this.name, {detail: {hiddenTypes}}));
+ console.log(`Event ${this.name} dispatched`);
+ }
+}
+
+export class MapOptionsChangedEvent {
+ static on(callback: (mapOptions: MapOptions) => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback(ev.detail.mapOptions);
+ });
+ }
+
+ static dispatch(mapOptions: MapOptions) {
+ document.dispatchEvent(new CustomEvent(this.name, {detail: {mapOptions}}));
+ console.log(`Event ${this.name} dispatched`);
+ }
+}
+
+export class MapSourceChangedEvent {
+ static on(callback: (source: string) => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback(ev.detail.source);
+ });
+ }
+
+ static dispatch(source: string) {
+ document.dispatchEvent(new CustomEvent(this.name, {detail: {source}}));
+ console.log(`Event ${this.name} dispatched`);
+ }
+}
+
+export class CoalitionAreaSelectedEvent {
+ static on(callback: (coalitionArea: CoalitionCircle | CoalitionPolygon | null) => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback(ev.detail.coalitionArea);
+ });
+ }
+
+ static dispatch(coalitionArea: CoalitionCircle | CoalitionPolygon | null) {
+ document.dispatchEvent(new CustomEvent(this.name, { detail: { coalitionArea } }));
+ console.log(`Event ${this.name} dispatched`);
+ }
+}
+
+export class AirbaseSelectedEvent {
+ static on(callback: (airbase: Airbase) => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback(ev.detail.airbase);
+ });
+ }
+
+ static dispatch(airbase: Airbase) {
+ document.dispatchEvent(new CustomEvent(this.name, { detail: { airbase } }));
+ console.log(`Event ${this.name} dispatched`);
+ }
+}
+
+export class ContactsUpdatedEvent {
+ static on(callback: () => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback();
+ });
+ }
+
+ static dispatch() {
+ document.dispatchEvent(new CustomEvent(this.name));
+ console.log(`Event ${this.name} dispatched`);
+ }
+}
+
+export class ContextActionSetChangedEvent {
+ static on(callback: (contextActionSet: ContextActionSet) => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback(ev.detail.contextActionSet);
+ });
+ }
+
+ static dispatch(contextActionSet: ContextActionSet) {
+ document.dispatchEvent(new CustomEvent(this.name, {detail: {contextActionSet}}));
+ console.log(`Event ${this.name} dispatched`);
+ }
+}
+
+export class ContextActionChangedEvent {
+ static on(callback: (contextAction: ContextAction) => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback(ev.detail.contextActionSet);
+ });
+ }
+
+ static dispatch(contextAction: ContextAction) {
+ document.dispatchEvent(new CustomEvent(this.name, {detail: {contextAction}}));
+ console.log(`Event ${this.name} dispatched`);
+ }
+}
+
+export class UnitUpdatedEvent extends BaseUnitEvent {};
+export class UnitSelectedEvent extends BaseUnitEvent {};
+export class UnitDeselectedEvent extends BaseUnitEvent {};
+export class UnitDeadEvent extends BaseUnitEvent {};
+export class SelectionClearedEvent extends BaseOlympusEvent {};
+
+export class SelectedUnitsChangedEvent {
+ static on(callback: (selectedUnits: Unit[]) => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback(ev.detail);
+ });
+ }
+
+ static dispatch(selectedUnits: Unit[]) {
+ document.dispatchEvent(new CustomEvent(this.name, {detail: selectedUnits}));
+ console.log(`Event ${this.name} dispatched`);
+ console.log(selectedUnits)
+ }
+}
+
+/************** Command mode events ***************/
+export class CommandModeOptionsChangedEvent {
+ /* TODO: add command mode options */
+ static on(callback: () => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback();
+ });
+ }
+
+ static dispatch() {
+ document.dispatchEvent(new CustomEvent(this.name));
+ console.log(`Event ${this.name} dispatched`);
+ }
+}
+
+/************** Audio backend events ***************/
+/* TODO: split into two events for signgle source changed */
+export class AudioSourcesChangedEvent {
+ /* TODO add audio sources */
+ static on(callback: (audioSources: AudioSource[]) => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback(ev.detail);
+ });
+ }
+
+ static dispatch(audioSources: AudioSource[]) {
+ document.dispatchEvent(new CustomEvent(this.name, {detail: {audioSources}}));
+ console.log(`Event ${this.name} dispatched`);
+ console.log(audioSources)
+ }
+}
+
+/* TODO: split into two events for signgle sink changed */
+export class AudioSinksChangedEvent {
+ static on(callback: (audioSinks: AudioSink[]) => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback(ev.detail);
+ });
+ }
+
+ static dispatch(audioSinks: AudioSink[]) {
+ document.dispatchEvent(new CustomEvent(this.name, {detail: {audioSinks}}));
+ console.log(`Event ${this.name} dispatched`);
+ console.log(audioSinks)
+ }
+}
+
+export class SRSClientsChangedEvent {
+ /* TODO add clients */
+ static on(callback: () => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback();
+ });
+ }
+
+ static dispatch() {
+ document.dispatchEvent(new CustomEvent(this.name));
+ // Logging disabled since periodic
+ }
+}
+
+export class AudioManagerStateChangedEvent {
+ static on(callback: (state: boolean) => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback(ev.detail.state);
+ });
+ }
+
+ static dispatch(state: boolean) {
+ document.dispatchEvent(new CustomEvent(this.name, {detail: {state}}));
+ console.log(`Event ${this.name} dispatched`);
+ }
+}
diff --git a/frontend/react/src/map/coalitionarea/coalitioncircle.ts b/frontend/react/src/map/coalitionarea/coalitioncircle.ts
index 73d888c0..217c928b 100644
--- a/frontend/react/src/map/coalitionarea/coalitioncircle.ts
+++ b/frontend/react/src/map/coalitionarea/coalitioncircle.ts
@@ -4,6 +4,7 @@ import { CoalitionAreaHandle } from "./coalitionareahandle";
import { BLUE_COMMANDER, RED_COMMANDER } from "../../constants/constants";
import { Coalition } from "../../types/types";
import * as turf from "@turf/turf";
+import { CoalitionAreaSelectedEvent } from "../../events";
let totalAreas = 0;
@@ -59,13 +60,7 @@ export class CoalitionCircle extends Circle {
this.#drawLabel();
this.setOpacity(selected ? 1 : 0.5);
- if (selected) {
- document.dispatchEvent(
- new CustomEvent("coalitionAreaSelected", {
- detail: this,
- })
- );
- }
+ if (selected) CoalitionAreaSelectedEvent.dispatch(this);
//@ts-ignore draggable option added by leaflet-path-drag
selected ? this.dragging.enable() : this.dragging.disable();
diff --git a/frontend/react/src/map/coalitionarea/coalitionpolygon.ts b/frontend/react/src/map/coalitionarea/coalitionpolygon.ts
index 9020bda6..32d64276 100644
--- a/frontend/react/src/map/coalitionarea/coalitionpolygon.ts
+++ b/frontend/react/src/map/coalitionarea/coalitionpolygon.ts
@@ -5,6 +5,7 @@ import { CoalitionAreaMiddleHandle } from "./coalitionareamiddlehandle";
import { BLUE_COMMANDER, RED_COMMANDER } from "../../constants/constants";
import { Coalition } from "../../types/types";
import { polyCenter } from "../../other/utils";
+import { CoalitionAreaSelectedEvent } from "../../events";
let totalAreas = 0;
@@ -70,13 +71,7 @@ export class CoalitionPolygon extends Polygon {
this.setEditing(false);
}
- if (selected) {
- document.dispatchEvent(
- new CustomEvent("coalitionAreaSelected", {
- detail: this,
- })
- );
- }
+ if (selected) CoalitionAreaSelectedEvent.dispatch(this);
//@ts-ignore draggable option added by leaflet-path-drag
selected ? this.dragging.enable() : this.dragging.disable();
diff --git a/frontend/react/src/map/map.ts b/frontend/react/src/map/map.ts
index 8c10e18e..cd9d00ad 100644
--- a/frontend/react/src/map/map.ts
+++ b/frontend/react/src/map/map.ts
@@ -37,6 +37,8 @@ import { faDrawPolygon, faHandPointer, faJetFighter, faMap } from "@fortawesome/
import { ExplosionMarker } from "./markers/explosionmarker";
import { TextMarker } from "./markers/textmarker";
import { TargetMarker } from "./markers/targetmarker";
+import { AppStateChangedEvent, CoalitionAreaSelectedEvent, ConfigLoadedEvent, HiddenTypesChangedEvent, MapOptionsChangedEvent, MapSourceChangedEvent } from "../events";
+import { ContextActionSet } from "../unit/contextactionset";
/* Register the handler for the box selection */
L.Map.addInitHook("addHandler", "boxSelect", BoxSelect);
@@ -105,8 +107,8 @@ export class Map extends L.Map {
#coalitionAreas: (CoalitionPolygon | CoalitionCircle)[] = [];
/* Unit context actions */
+ #contextActionSet: null | ContextActionSet = null;
#contextAction: null | ContextAction = null;
- #defaultContextAction: null | ContextAction = null;
/* Unit spawning */
#spawnRequestTable: SpawnRequestTable | null = null;
@@ -181,11 +183,9 @@ export class Map extends L.Map {
L.DomEvent.on(this.getContainer(), "touchend", this.#onMouseUp, this);
/* Event listeners */
- document.addEventListener("appStateChanged", (ev: CustomEventInit) => {
- this.#onStateChanged(ev.detail.state, ev.detail.subState);
- });
+ AppStateChangedEvent.on((state, subState) => this.#onStateChanged(state, subState));
- document.addEventListener("hiddenTypesChanged", (ev: CustomEventInit) => {
+ HiddenTypesChangedEvent.on((hiddenTypes) => {
Object.values(getApp().getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
Object.values(getApp().getMissionManager().getAirbases()).forEach((airbase: Airbase) => {
if (this.getHiddenTypes().airbase) airbase.removeFrom(this);
@@ -198,10 +198,10 @@ export class Map extends L.Map {
// this.#panToUnit(this.#centerUnit);
//});
- document.addEventListener("mapOptionsChanged", () => {
- this.getContainer().toggleAttribute("data-hide-labels", !this.getOptions().showUnitLabels);
- //this.#cameraControlPort = this.getOptions()[DCS_LINK_PORT] as number;
- //this.#cameraZoomRatio = 50 / (20 + (this.getOptions()[DCS_LINK_RATIO] as number));
+ MapOptionsChangedEvent.on((options) => {
+ this.getContainer().toggleAttribute("data-hide-labels", !options.showUnitLabels);
+ //this.#cameraControlPort = options[DCS_LINK_PORT] as number;
+ //this.#cameraZoomRatio = 50 / (20 + (options[DCS_LINK_RATIO] as number));
if (this.#slaveDCSCamera) {
this.#broadcastPosition();
@@ -213,7 +213,7 @@ export class Map extends L.Map {
this.updateMinimap();
});
- document.addEventListener("configLoaded", () => {
+ ConfigLoadedEvent.on(() => {
let config = getApp().getConfig();
let layerSet = false;
@@ -248,73 +248,6 @@ export class Map extends L.Map {
}
});
- document.addEventListener("toggleCameraLinkStatus", () => {
- this.setSlaveDCSCamera(!this.#slaveDCSCamera);
- });
-
- document.addEventListener("slewCameraToPosition", () => {
- this.#broadcastPosition();
- });
-
- document.addEventListener("selectJTACECHO", (ev: CustomEventInit) => {
- if (!this.#ECHOPoint) {
- this.#ECHOPoint = new TextMarker(ev.detail, "BP", "rgb(37 99 235)", { interactive: true, draggable: true });
- this.#ECHOPoint.addTo(this);
- this.#ECHOPoint.on("dragstart", (event) => {
- event.target.options["freeze"] = true;
- });
- this.#ECHOPoint.on("dragend", (event) => {
- document.dispatchEvent(new CustomEvent("selectJTACECHO", { detail: this.#ECHOPoint?.getLatLng() }));
- event.target.options["freeze"] = false;
- });
- this.#ECHOPoint.on("click", (event) => {
- getApp().setState(OlympusState.JTAC)
- })
- } else this.#ECHOPoint.setLatLng(ev.detail);
- });
-
- document.addEventListener("selectJTACIP", (ev: CustomEventInit) => {
- if (!this.#IPPoint) {
- this.#IPPoint = new TextMarker(ev.detail, "IP", "rgb(168 85 247)", { interactive: true, draggable: true });
- this.#IPPoint.addTo(this);
- this.#IPPoint.on("dragstart", (event) => {
- event.target.options["freeze"] = true;
- });
- this.#IPPoint.on("dragend", (event) => {
- document.dispatchEvent(new CustomEvent("selectJTACIP", { detail: this.#IPPoint?.getLatLng() }));
- event.target.options["freeze"] = false;
- });
- this.#IPPoint.on("click", (event) => {
- getApp().setState(OlympusState.JTAC)
- })
- } else this.#IPPoint.setLatLng(ev.detail);
-
- this.#drawIPToTargetLine();
- });
-
- document.addEventListener("selectJTACTarget", (ev: CustomEventInit) => {
- if (ev.detail.location) {
- if (!this.#targetPoint) {
- this.#targetPoint = new TargetMarker(ev.detail.location, { interactive: true, draggable: true });
- this.#targetPoint.addTo(this);
- this.#targetPoint.on("dragstart", (event) => {
- event.target.options["freeze"] = true;
- });
- this.#targetPoint.on("dragend", (event) => {
- document.dispatchEvent(new CustomEvent("selectJTACTarget", { detail: { location: this.#targetPoint?.getLatLng() } }));
- event.target.options["freeze"] = false;
- });
- this.#targetPoint.on("click", (event) => {
- getApp().setState(OlympusState.JTAC)
- })
- } else this.#targetPoint.setLatLng(ev.detail.location);
- } else {
- this.#targetPoint?.removeFrom(this);
- this.#targetPoint = null;
- }
- this.#drawIPToTargetLine();
- });
-
/* Pan interval */
this.#panInterval = window.setInterval(() => {
if (this.#panUp || this.#panDown || this.#panRight || this.#panLeft)
@@ -395,7 +328,7 @@ export class Map extends L.Map {
}
this.#layerName = layerName;
- document.dispatchEvent(new CustomEvent("mapSourceChanged", { detail: layerName }));
+ MapSourceChangedEvent.dispatch(layerName);
}
getLayerName() {
@@ -414,12 +347,12 @@ export class Map extends L.Map {
this.#effectRequestTable = effectRequestTable;
}
- setContextAction(contextAction: ContextAction | null) {
- this.#contextAction = contextAction;
+ setContextActionSet(contextActionSet: ContextActionSet | null) {
+ this.#contextActionSet = contextActionSet;
}
- setDefaultContextAction(defaultContextAction: ContextAction | null) {
- this.#defaultContextAction = defaultContextAction;
+ setContextAction(contextAction: ContextAction | null) {
+ this.#contextAction = contextAction;
}
#onStateChanged(state: OlympusState, subState: OlympusSubState) {
@@ -459,13 +392,11 @@ export class Map extends L.Map {
} else if (state === OlympusState.UNIT_CONTROL) {
console.log(`Context action:`);
console.log(this.#contextAction);
- console.log(`Default context action callback:`);
- console.log(this.#defaultContextAction);
} else if (state === OlympusState.DRAW) {
if (subState == DrawSubState.DRAW_POLYGON) {
this.#coalitionAreas.push(new CoalitionPolygon([]));
this.#coalitionAreas[this.#coalitionAreas.length - 1].addTo(this);
- this.#coalitionAreas[this.#coalitionAreas.length - 1].setSelected(true);
+ this.#coalitionAreas[this.#coalitionAreas.length - 1].setSelected(true);
} else if (subState === DrawSubState.DRAW_CIRCLE) {
this.#coalitionAreas.push(new CoalitionCircle(new L.LatLng(0, 0), { radius: 1000 }));
this.#coalitionAreas[this.#coalitionAreas.length - 1].addTo(this);
@@ -683,12 +614,7 @@ export class Map extends L.Map {
}
deselectAllCoalitionAreas() {
- document.dispatchEvent(
- new CustomEvent("coalitionAreaSelected", {
- detail: null,
- })
- );
-
+ CoalitionAreaSelectedEvent.dispatch(null);
this.#coalitionAreas.forEach((coalitionArea: CoalitionPolygon | CoalitionCircle) => coalitionArea.setSelected(false));
}
@@ -700,7 +626,7 @@ export class Map extends L.Map {
setHiddenType(key: string, value: boolean) {
this.#hiddenTypes[key] = value;
- document.dispatchEvent(new CustomEvent("hiddenTypesChanged"));
+ HiddenTypesChangedEvent.dispatch(this.#hiddenTypes);
}
getHiddenTypes() {
@@ -847,7 +773,7 @@ export class Map extends L.Map {
setOption(key, value) {
this.#options[key] = value;
- document.dispatchEvent(new CustomEvent("mapOptionsChanged"));
+ MapOptionsChangedEvent.dispatch(this.#options);
}
getOptions() {
@@ -906,16 +832,16 @@ export class Map extends L.Map {
this.#contextAction?.executeCallback(targetUnit, targetPosition);
}
+ getContextActionSet() {
+ return this.#contextActionSet;
+ }
+
getContextAction() {
return this.#contextAction;
}
executeDefaultContextAction(targetUnit: Unit | null, targetPosition: L.LatLng | null) {
- if (this.#defaultContextAction) this.#defaultContextAction.executeCallback(targetUnit, targetPosition);
- }
-
- getDefaultContextAction() {
- return this.#defaultContextAction;
+ this.#contextActionSet?.getDefaultContextAction()?.executeCallback(targetUnit, targetPosition);
}
preventClicks() {
@@ -939,7 +865,6 @@ export class Map extends L.Map {
#onSelectionEnd(e: any) {
getApp().getUnitsManager().selectFromBounds(e.selectionBounds);
- document.dispatchEvent(new CustomEvent("mapSelectionEnd"));
this.#selecting = false;
}
@@ -1037,7 +962,7 @@ export class Map extends L.Map {
for (let idx = 0; idx < this.#coalitionAreas.length; idx++) {
if (areaContains(pressLocation, this.#coalitionAreas[idx])) {
this.#coalitionAreas[idx].setSelected(true);
- getApp().setState(OlympusState.DRAW, DrawSubState.EDIT)
+ getApp().setState(OlympusState.DRAW, DrawSubState.EDIT);
break;
}
}
@@ -1047,17 +972,57 @@ export class Map extends L.Map {
if (this.#contextAction !== null) this.executeContextAction(null, pressLocation);
else getApp().setState(OlympusState.IDLE);
} else if (e.originalEvent.buttons === 2) {
- if (this.#defaultContextAction !== null) this.executeDefaultContextAction(null, pressLocation);
+ this.executeDefaultContextAction(null, pressLocation);
}
} else if (getApp().getState() === OlympusState.JTAC) {
if (getApp().getSubState() === JTACSubState.SELECT_TARGET) {
- document.dispatchEvent(new CustomEvent("selectJTACTarget", { detail: { location: pressLocation } }));
+ if (!this.#targetPoint) {
+ this.#targetPoint = new TextMarker(pressLocation, "BP", "rgb(37 99 235)", { interactive: true, draggable: true });
+ this.#targetPoint.addTo(this);
+ this.#targetPoint.on("dragstart", (event) => {
+ event.target.options["freeze"] = true;
+ });
+ this.#targetPoint.on("dragend", (event) => {
+ getApp().setState(OlympusState.JTAC);
+ event.target.options["freeze"] = false;
+ });
+ this.#targetPoint.on("click", (event) => {
+ getApp().setState(OlympusState.JTAC);
+ });
+ } else this.#targetPoint.setLatLng(pressLocation);
} else if (getApp().getSubState() === JTACSubState.SELECT_ECHO_POINT) {
- document.dispatchEvent(new CustomEvent("selectJTACECHO", { detail: pressLocation }));
+ if (!this.#ECHOPoint) {
+ this.#ECHOPoint = new TextMarker(pressLocation, "BP", "rgb(37 99 235)", { interactive: true, draggable: true });
+ this.#ECHOPoint.addTo(this);
+ this.#ECHOPoint.on("dragstart", (event) => {
+ event.target.options["freeze"] = true;
+ });
+ this.#ECHOPoint.on("dragend", (event) => {
+ getApp().setState(OlympusState.JTAC);
+ event.target.options["freeze"] = false;
+ });
+ this.#ECHOPoint.on("click", (event) => {
+ getApp().setState(OlympusState.JTAC);
+ });
+ } else this.#ECHOPoint.setLatLng(pressLocation);
} else if (getApp().getSubState() === JTACSubState.SELECT_IP) {
- document.dispatchEvent(new CustomEvent("selectJTACIP", { detail: pressLocation }));
+ if (!this.#IPPoint) {
+ this.#IPPoint = new TextMarker(pressLocation, "BP", "rgb(37 99 235)", { interactive: true, draggable: true });
+ this.#IPPoint.addTo(this);
+ this.#IPPoint.on("dragstart", (event) => {
+ event.target.options["freeze"] = true;
+ });
+ this.#IPPoint.on("dragend", (event) => {
+ getApp().setState(OlympusState.JTAC);
+ event.target.options["freeze"] = false;
+ });
+ this.#IPPoint.on("click", (event) => {
+ getApp().setState(OlympusState.JTAC);
+ });
+ } else this.#IPPoint.setLatLng(pressLocation);
}
getApp().setState(OlympusState.JTAC);
+ this.#drawIPToTargetLine();
} else {
}
}
diff --git a/frontend/react/src/mission/airbase.ts b/frontend/react/src/mission/airbase.ts
index 7e3e0655..31e198fb 100644
--- a/frontend/react/src/mission/airbase.ts
+++ b/frontend/react/src/mission/airbase.ts
@@ -4,6 +4,7 @@ import { SVGInjector } from "@tanem/svg-injector";
import { AirbaseChartData, AirbaseOptions } from "../interfaces";
import { getApp } from "../olympusapp";
import { OlympusState } from "../constants/constants";
+import { AirbaseSelectedEvent } from "../events";
export class Airbase extends CustomMarker {
#name: string = "";
@@ -27,7 +28,7 @@ export class Airbase extends CustomMarker {
this.addEventListener("click", (ev) => {
if (getApp().getState() === OlympusState.IDLE) {
getApp().setState(OlympusState.AIRBASE)
- // TODO: document.dispatchEvent(new CustomEvent("airbaseClick", { detail: ev.target }));
+ AirbaseSelectedEvent.dispatch(this)
}
});
}
@@ -48,12 +49,6 @@ export class Airbase extends CustomMarker {
this.#img.onload = () => SVGInjector(this.#img);
el.appendChild(this.#img);
this.getElement()?.appendChild(el);
- el.addEventListener("mouseover", (ev) => {
- document.dispatchEvent(new CustomEvent("airbasemouseover", { detail: this }));
- });
- el.addEventListener("mouseout", (ev) => {
- document.dispatchEvent(new CustomEvent("airbasemouseout", { detail: this }));
- });
el.dataset.coalition = this.#coalition;
}
diff --git a/frontend/react/src/mission/carrier.ts b/frontend/react/src/mission/carrier.ts
index 35b5412b..74d15084 100644
--- a/frontend/react/src/mission/carrier.ts
+++ b/frontend/react/src/mission/carrier.ts
@@ -18,12 +18,6 @@ export class Carrier extends Airbase {
this.getImg().style.width = `0px`; // Make the image immediately small to avoid giant carriers
el.appendChild(this.getImg());
this.getElement()?.appendChild(el);
- el.addEventListener("mouseover", (ev) => {
- document.dispatchEvent(new CustomEvent("airbasemouseover", { detail: this }));
- });
- el.addEventListener("mouseout", (ev) => {
- document.dispatchEvent(new CustomEvent("airbasemouseout", { detail: this }));
- });
el.dataset.coalition = this.getCoalition();
}
diff --git a/frontend/react/src/mission/missionmanager.ts b/frontend/react/src/mission/missionmanager.ts
index 1167a38b..67d49f78 100644
--- a/frontend/react/src/mission/missionmanager.ts
+++ b/frontend/react/src/mission/missionmanager.ts
@@ -14,6 +14,7 @@ import { AirbasesData, BullseyesData, CommandModeOptions, DateAndTime, MissionDa
import { Coalition } from "../types/types";
import { Carrier } from "./carrier";
import { NavyUnit } from "../unit/unit";
+import { CommandModeOptionsChangedEvent } from "../events";
/** The MissionManager */
export class MissionManager {
@@ -42,17 +43,7 @@ export class MissionManager {
//#commandModeErasDropdown: Dropdown;
#coalitions: { red: string[]; blue: string[] } = { red: [], blue: [] };
- constructor() {
- document.addEventListener("applycommandModeOptions", () => this.#applycommandModeOptions());
- document.addEventListener("showCommandModeDialog", () => this.showCommandModeDialog());
- document.addEventListener("toggleSpawnRestrictions", (ev: CustomEventInit) => {
- this.#toggleSpawnRestrictions(ev.detail._element.checked);
- });
-
- /* command-mode settings dialog */
- //this.#commandModeDialog = document.querySelector("#command-mode-settings-dialog") as HTMLElement;
- //this.#commandModeErasDropdown = new Dropdown("command-mode-era-options", () => {});
- }
+ constructor() {}
/** Update location of bullseyes
*
@@ -303,13 +294,7 @@ export class MissionManager {
this.refreshSpawnPoints();
if (commandModeOptionsChanged) {
- document.dispatchEvent(new CustomEvent("commandModeOptionsChanged", { detail: this }));
- document.getElementById("command-mode-toolbar")?.classList.remove("hide");
- const el = document.getElementById("command-mode");
- if (el) {
- el.dataset.mode = commandModeOptions.commandMode;
- el.textContent = commandModeOptions.commandMode.toUpperCase();
- }
+ CommandModeOptionsChangedEvent.dispatch();
}
document
diff --git a/frontend/react/src/olympusapp.ts b/frontend/react/src/olympusapp.ts
index 84ebaf47..319b2462 100644
--- a/frontend/react/src/olympusapp.ts
+++ b/frontend/react/src/olympusapp.ts
@@ -27,12 +27,12 @@ import { groundUnitDatabase } from "./unit/databases/groundunitdatabase";
import { navyUnitDatabase } from "./unit/databases/navyunitdatabase";
import { Coalition, Context } from "./types/types";
import { Unit } from "./unit/unit";
+import { AppStateChangedEvent, ConfigLoadedEvent, SelectedUnitsChangedEvent } from "./events";
export var VERSION = "{{OLYMPUS_VERSION_NUMBER}}";
export var IP = window.location.toString();
export var connectedToServer = true; // TODO Temporary
-
export class OlympusApp {
/* Global data */
#latestVersion: string | undefined = undefined;
@@ -42,7 +42,7 @@ export class OlympusApp {
#events = {
[OlympusEvent.STATE_CHANGED]: [] as ((state: OlympusState, subState: OlympusSubState) => void)[],
- [OlympusEvent.UNITS_SELECTED]: [] as ((units: Unit[]) => void)[]
+ [OlympusEvent.UNITS_SELECTED]: [] as ((units: Unit[]) => void)[],
};
/* Main leaflet map, extended by custom methods */
@@ -60,7 +60,12 @@ export class OlympusApp {
/* Current context */
#context: Context = DEFAULT_CONTEXT;
- constructor() {}
+ constructor() {
+ SelectedUnitsChangedEvent.on((selectedUnits) => {
+ if (selectedUnits.length > 0) this.setState(OlympusState.UNIT_CONTROL);
+ else this.getState() === OlympusState.UNIT_CONTROL && this.setState(OlympusState.IDLE)
+ });
+ }
getCurrentContext() {
return this.#context;
@@ -178,10 +183,9 @@ export class OlympusApp {
})
.then((res) => {
this.#config = res;
- document.dispatchEvent(new CustomEvent("configLoaded"));
+ ConfigLoadedEvent.dispatch(); // TODO actually dispatch the config
this.setState(OlympusState.LOGIN);
});
-
}
getConfig() {
@@ -192,8 +196,8 @@ export class OlympusApp {
this.#state = state;
this.#subState = subState;
- console.log(`App state set to ${state}, substate ${subState}`)
- this.dispatchEvent(OlympusEvent.STATE_CHANGED, state, subState)
+ console.log(`App state set to ${state}, substate ${subState}`);
+ AppStateChangedEvent.dispatch(state, subState);
}
getState() {
@@ -203,15 +207,4 @@ export class OlympusApp {
getSubState() {
return this.#subState;
}
-
- registerEventCallback(event: OlympusEvent, callback: any) {
- this.#events[event].push(callback)
- }
-
- dispatchEvent(event: OlympusEvent, ...args) {
- console.log(`Dispatching event ${event}. Arguments: ${args}`)
- this.#events[event].forEach((event) => {
- event(args);
- })
- }
}
diff --git a/frontend/react/src/server/servermanager.ts b/frontend/react/src/server/servermanager.ts
index 1d4f8d02..2cd64ad3 100644
--- a/frontend/react/src/server/servermanager.ts
+++ b/frontend/react/src/server/servermanager.ts
@@ -14,6 +14,7 @@ import {
reactionsToThreat,
} from "../constants/constants";
import { AirbasesData, BullseyesData, GeneralSettings, MissionData, Radio, ServerRequestOptions, ServerStatus, TACAN } from "../interfaces";
+import { ServerStatusUpdatedEvent } from "../events";
export class ServerManager {
#connected: boolean = false;
@@ -142,7 +143,7 @@ export class ServerManager {
setAddress(address: string) {
this.#REST_ADDRESS = `${address.replace("vite/", "").replace("vite", "")}olympus`;
-
+
console.log(`Setting REST address to ${this.#REST_ADDRESS}`);
}
@@ -610,18 +611,14 @@ export class ServerManager {
this.#serverIsPaused = elapsedMissionTime === this.#previousMissionElapsedTime;
this.#previousMissionElapsedTime = elapsedMissionTime;
- document.dispatchEvent(
- new CustomEvent("serverStatusUpdated", {
- detail: {
- frameRate: getApp().getMissionManager().getFrameRate(),
- load: getApp().getMissionManager().getLoad(),
- elapsedTime: getApp().getMissionManager().getDateAndTime().elapsedTime,
- missionTime: getApp().getMissionManager().getDateAndTime().time,
- connected: this.getConnected(),
- paused: this.getPaused(),
- } as ServerStatus,
- })
- );
+ ServerStatusUpdatedEvent.dispatch({
+ frameRate: getApp().getMissionManager().getFrameRate(),
+ load: getApp().getMissionManager().getLoad(),
+ elapsedTime: getApp().getMissionManager().getDateAndTime().elapsedTime,
+ missionTime: getApp().getMissionManager().getDateAndTime().time,
+ connected: this.getConnected(),
+ paused: this.getPaused(),
+ } as ServerStatus);
}, 1000)
);
diff --git a/frontend/react/src/statecontext.tsx b/frontend/react/src/statecontext.tsx
index a6f93ec9..1e7b1755 100644
--- a/frontend/react/src/statecontext.tsx
+++ b/frontend/react/src/statecontext.tsx
@@ -1,5 +1,11 @@
import { createContext } from "react";
import { MAP_HIDDEN_TYPES_DEFAULTS, MAP_OPTIONS_DEFAULTS, NO_SUBSTATE, OlympusState, OlympusSubState } from "./constants/constants";
+import { Unit } from "./unit/unit";
+import { AudioSource } from "./audio/audiosource";
+import { AudioSink } from "./audio/audiosink";
+import { ServerStatus } from "./interfaces";
+import { ContextActionSet } from "./unit/contextactionset";
+import { ContextAction } from "./unit/contextaction";
export const StateContext = createContext({
appState: OlympusState.NOT_INITIALIZED as OlympusState,
@@ -7,7 +13,14 @@ export const StateContext = createContext({
mapHiddenTypes: MAP_HIDDEN_TYPES_DEFAULTS,
mapOptions: MAP_OPTIONS_DEFAULTS,
mapSources: [] as string[],
- activeMapSource: ""
+ activeMapSource: "",
+ selectedUnits: [] as Unit[],
+ audioSources: [] as AudioSource[],
+ audioSinks: [] as AudioSink[],
+ audioManagerState: false,
+ serverStatus: {} as ServerStatus,
+ contextActionSet: null as ContextActionSet | null,
+ contextAction: null as ContextAction | null
});
export const StateProvider = StateContext.Provider;
diff --git a/frontend/react/src/ui/contextmenus/mapcontextmenu.tsx b/frontend/react/src/ui/contextmenus/mapcontextmenu.tsx
index ec39e50a..351571a4 100644
--- a/frontend/react/src/ui/contextmenus/mapcontextmenu.tsx
+++ b/frontend/react/src/ui/contextmenus/mapcontextmenu.tsx
@@ -7,6 +7,7 @@ import { CONTEXT_ACTION_COLORS } from "../../constants/constants";
import { OlDropdownItem } from "../components/oldropdown";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { LatLng } from "leaflet";
+import { SelectionClearedEvent } from "../../events";
export function MapContextMenu(props: {}) {
const [open, setOpen] = useState(false);
@@ -71,7 +72,7 @@ export function MapContextMenu(props: {}) {
setOpen(false);
});
- document.addEventListener("clearSelection", () => {
+ SelectionClearedEvent.on(() => {
setOpen(false);
});
}, []);
diff --git a/frontend/react/src/ui/panels/audiomenu.tsx b/frontend/react/src/ui/panels/audiomenu.tsx
index 5b716e01..16ef6f10 100644
--- a/frontend/react/src/ui/panels/audiomenu.tsx
+++ b/frontend/react/src/ui/panels/audiomenu.tsx
@@ -11,6 +11,7 @@ import { UnitSinkPanel } from "./components/unitsinkpanel";
import { UnitSink } from "../../audio/unitsink";
import { FaMinus, FaVolumeHigh } from "react-icons/fa6";
import { getRandomColor } from "../../other/utils";
+import { AudioManagerStateChangedEvent, AudioSinksChangedEvent, AudioSourcesChangedEvent } from "../../events";
let shortcutKeys = ["Z", "X", "C", "V", "B", "N", "M", "K", "L"];
@@ -36,7 +37,7 @@ export function AudioMenu(props: { open: boolean; onClose: () => void; children?
useEffect(() => {
/* Force a rerender */
- document.addEventListener("audioSinksUpdated", () => {
+ AudioSinksChangedEvent.on(() => {
setSinks(
getApp()
?.getAudioManager()
@@ -47,7 +48,7 @@ export function AudioMenu(props: { open: boolean; onClose: () => void; children?
});
/* Force a rerender */
- document.addEventListener("audioSourcesUpdated", () => {
+ AudioSourcesChangedEvent.on(() => {
setSources(
getApp()
?.getAudioManager()
@@ -56,7 +57,7 @@ export function AudioMenu(props: { open: boolean; onClose: () => void; children?
);
});
- document.addEventListener("audioManagerStateChanged", () => {
+ AudioManagerStateChangedEvent.on(() => {
setAudioManagerEnabled(getApp().getAudioManager().isRunning());
});
}, []);
diff --git a/frontend/react/src/ui/panels/controlspanel.tsx b/frontend/react/src/ui/panels/controlspanel.tsx
index 20f1119b..d121d4be 100644
--- a/frontend/react/src/ui/panels/controlspanel.tsx
+++ b/frontend/react/src/ui/panels/controlspanel.tsx
@@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react";
import { getApp } from "../../olympusapp";
import { IconDefinition } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { AppStateChangedEvent } from "../../events";
export function ControlsPanel(props: {}) {
const [controls, setControls] = useState(
@@ -19,7 +20,7 @@ export function ControlsPanel(props: {}) {
});
useEffect(() => {
- document.addEventListener("appStateChanged", (ev) => {
+ AppStateChangedEvent.on(() => {
setControls(getApp().getMap().getCurrentControls());
});
}, []);
diff --git a/frontend/react/src/ui/panels/drawingmenu.tsx b/frontend/react/src/ui/panels/drawingmenu.tsx
index 3c6c69b2..1465c273 100644
--- a/frontend/react/src/ui/panels/drawingmenu.tsx
+++ b/frontend/react/src/ui/panels/drawingmenu.tsx
@@ -13,10 +13,10 @@ import { Coalition } from "../../types/types";
import { OlRangeSlider } from "../components/olrangeslider";
import { CoalitionCircle } from "../../map/coalitionarea/coalitioncircle";
import { DrawSubState, NO_SUBSTATE, OlympusState } from "../../constants/constants";
+import { StateConsumer } from "../../statecontext";
+import { CoalitionAreaSelectedEvent } from "../../events";
export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
- const [appState, setAppState] = useState(OlympusState.NOT_INITIALIZED);
- const [appSubState, setAppSubState] = useState(NO_SUBSTATE);
const [activeCoalitionArea, setActiveCoalitionArea] = useState(null as null | CoalitionPolygon | CoalitionCircle);
const [areaCoalition, setAreaCoalition] = useState("blue" as Coalition);
const [IADSDensity, setIADSDensity] = useState(50);
@@ -32,276 +32,275 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
// TODO
///* If we are not in polygon drawing mode, force the draw polygon button off */
//if (drawingPolygon && getApp().getState() !== COALITIONAREA_DRAW_POLYGON) setDrawingPolygon(false);
-//
+ //
///* If we are not in circle drawing mode, force the draw circle button off */
//if (drawingCircle && getApp().getState() !== COALITIONAREA_DRAW_CIRCLE) setDrawingCircle(false);
-//
+ //
///* If we are not in any drawing mode, force the map in edit mode */
//if (props.open && !drawingPolygon && !drawingCircle) getApp().getMap().setState(COALITIONAREA_EDIT);
-//
+ //
///* Align the state of the coalition toggle to the coalition of the area */
//if (activeCoalitionArea && activeCoalitionArea?.getCoalition() !== areaCoalition) setAreaCoalition(activeCoalitionArea?.getCoalition());
}
});
useEffect(() => {
- document.addEventListener("coalitionAreaSelected", (event: any) => {
- setActiveCoalitionArea(event.detail);
- });
-
- document.addEventListener("appStateChanged", (ev: CustomEventInit) => {
- setAppState(ev.detail.state);
- setAppSubState(ev.detail.subState);
- });
+ CoalitionAreaSelectedEvent.on((coalitionArea) => setActiveCoalitionArea(coalitionArea));
}, []);
return (
-