diff --git a/frontend/react/src/audio/audiomanager.ts b/frontend/react/src/audio/audiomanager.ts
index 928172b7..92202606 100644
--- a/frontend/react/src/audio/audiomanager.ts
+++ b/frontend/react/src/audio/audiomanager.ts
@@ -199,7 +199,7 @@ export class AudioManager {
addRadio() {
console.log("Adding new radio");
- if (!this.#running) {
+ if (!this.#running || this.#sources[0] === undefined) {
console.log("Audio manager not started, aborting...");
return;
}
diff --git a/frontend/react/src/audio/radiosink.ts b/frontend/react/src/audio/radiosink.ts
index eb0af37f..cf75253d 100644
--- a/frontend/react/src/audio/radiosink.ts
+++ b/frontend/react/src/audio/radiosink.ts
@@ -13,7 +13,7 @@ export class RadioSink extends AudioSink {
#frequency = 251000000;
#modulation = 0;
#ptt = false;
- #tuned = false;
+ #tuned = true;
#volume = 0.5;
#receiving = false;
#clearReceivingTimeout: number;
diff --git a/frontend/react/src/constants/constants.ts b/frontend/react/src/constants/constants.ts
index 826387a7..06f6f031 100644
--- a/frontend/react/src/constants/constants.ts
+++ b/frontend/react/src/constants/constants.ts
@@ -529,8 +529,8 @@ export namespace ContextActions {
executeImmediately: true,
type: ContextActionType.DELETE,
code: "Delete",
- ctrlKey: true,
- shiftKey: false,
+ ctrlKey: false,
+ shiftKey: true,
altKey: false,
}
);
@@ -562,7 +562,7 @@ export namespace ContextActions {
export const FOLLOW = new ContextAction(
"follow",
"Follow unit",
- "Click on a unit to follow it in formation",
+ "Right-click on a unit to follow it in formation",
olButtonsContextFollow,
ContextActionTarget.UNIT,
(units: Unit[], targetUnit: Unit | null, _) => {
@@ -580,7 +580,7 @@ export namespace ContextActions {
export const BOMB = new ContextAction(
"bomb",
"Precision bomb location",
- "Click on a point to execute a precision bombing attack",
+ "Right-click on a point to execute a precision bombing attack",
faLocationCrosshairs,
ContextActionTarget.POINT,
(units: Unit[], _, targetPosition: LatLng | null) => {
@@ -595,7 +595,7 @@ export namespace ContextActions {
export const CARPET_BOMB = new ContextAction(
"carpet-bomb",
"Carpet bomb location",
- "Click on a point to execute a carpet bombing attack",
+ "Right-click on a point to execute a carpet bombing attack",
faXmarksLines,
ContextActionTarget.POINT,
(units: Unit[], _, targetPosition: LatLng | null) => {
@@ -610,7 +610,7 @@ export namespace ContextActions {
export const LAND = new ContextAction(
"land",
"Land",
- "Click on a point to land at the nearest airbase",
+ "Right-click on a point to land at the nearest airbase",
faPlaneArrival,
ContextActionTarget.POINT,
(units: Unit[], _, targetPosition: LatLng | null) => {
@@ -622,7 +622,7 @@ export namespace ContextActions {
export const LAND_AT_POINT = new ContextAction(
"land-at-point",
"Land at location",
- "Click on a point to land there",
+ "Right-click on a point to land there",
olButtonsContextLandAtPoint,
ContextActionTarget.POINT,
(units: Unit[], _, targetPosition: LatLng | null) => {
@@ -649,7 +649,7 @@ export namespace ContextActions {
export const ATTACK = new ContextAction(
"attack",
"Attack unit",
- "Click on a unit to attack it",
+ "Right-click on a unit to attack it",
olButtonsContextAttack,
ContextActionTarget.UNIT,
(units: Unit[], targetUnit: Unit | null, _) => {
@@ -661,7 +661,7 @@ export namespace ContextActions {
export const FIRE_AT_AREA = new ContextAction(
"fire-at-area",
"Fire at area",
- "Click on a point to precisely fire at it (if possible)",
+ "Right-click on a point to precisely fire at it (if possible)",
faLocationCrosshairs,
ContextActionTarget.POINT,
(units: Unit[], _, targetPosition: LatLng | null) => {
diff --git a/frontend/react/src/events.ts b/frontend/react/src/events.ts
index 9c8b3421..de9d0309 100644
--- a/frontend/react/src/events.ts
+++ b/frontend/react/src/events.ts
@@ -137,6 +137,19 @@ export class BindShortcutRequestEvent {
}
}
+export class ModalEvent {
+ static on(callback: (modal: boolean) => void) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback(ev.detail.modal);
+ });
+ }
+
+ static dispatch(modal: boolean) {
+ document.dispatchEvent(new CustomEvent(this.name, { detail: { modal } }));
+ console.log(`Event ${this.name} dispatched`);
+ }
+}
+
/************** Map events ***************/
export class HiddenTypesChangedEvent {
static on(callback: (hiddenTypes: MapHiddenTypes) => void) {
diff --git a/frontend/react/src/map/boxselect.ts b/frontend/react/src/map/boxselect.ts
index d525014a..b8cb922e 100644
--- a/frontend/react/src/map/boxselect.ts
+++ b/frontend/react/src/map/boxselect.ts
@@ -5,9 +5,10 @@ import { DomEvent } from "leaflet";
import { LatLngBounds } from "leaflet";
import { Bounds } from "leaflet";
import { SELECT_TOLERANCE_PX } from "../constants/constants";
+import { Map } from "./map";
export var BoxSelect = Handler.extend({
- initialize: function (map) {
+ initialize: function (map: Map) {
this._map = map;
this._container = map.getContainer();
this._pane = map.getPanes().overlayPane;
@@ -45,13 +46,12 @@ export var BoxSelect = Handler.extend({
},
_onMouseDown: function (e: any) {
- if (e.which == 1 && e.button == 0) {
+ if (this._map.getEnableSelection() && e.button == 0) {
// Clear the deferred resetState if it hasn't executed yet, otherwise it
// will interrupt the interaction and orphan a box element in the container.
this._clearDeferredResetState();
this._resetState();
- DomUtil.disableTextSelection();
DomUtil.disableImageDrag();
this._map.dragging.disable();
@@ -65,10 +65,8 @@ export var BoxSelect = Handler.extend({
contextmenu: DomEvent.stop,
touchmove: this._onMouseMove,
touchend: this._onMouseUp,
- touchstart: this._onKeyDown,
mousemove: this._onMouseMove,
- mouseup: this._onMouseUp,
- keydown: this._onKeyDown,
+ mouseup: this._onMouseUp
},
this
);
@@ -77,6 +75,24 @@ export var BoxSelect = Handler.extend({
}
},
+ _onMouseUp: function (e: any) {
+ if (e.button !== 0) {
+ return;
+ }
+
+ this._finish();
+
+ if (!this._moved) {
+ return;
+ }
+ // Postpone to next JS tick so internal click event handling
+ // still see it as "moved".
+ window.setTimeout(Util.bind(this._resetState, this), 0);
+ var bounds = new LatLngBounds(this._map.containerPointToLatLng(this._startPoint), this._map.containerPointToLatLng(this._point));
+
+ this._map.fire("selectionend", { selectionBounds: bounds });
+ },
+
_onMouseMove: function (e: any) {
if (e.type === "touchmove") this._point = this._map.mouseEventToContainerPoint(e.touches[0]);
else this._point = this._map.mouseEventToContainerPoint(e);
@@ -110,7 +126,6 @@ export var BoxSelect = Handler.extend({
DomUtil.removeClass(this._container, "leaflet-crosshair");
}
- DomUtil.enableTextSelection();
DomUtil.enableImageDrag();
this._map.dragging.enable();
@@ -121,38 +136,10 @@ export var BoxSelect = Handler.extend({
contextmenu: DomEvent.stop,
touchmove: this._onMouseMove,
touchend: this._onMouseUp,
- touchstart: this._onKeyDown,
mousemove: this._onMouseMove,
mouseup: this._onMouseUp,
- keydown: this._onKeyDown,
},
this
);
},
-
- _onMouseUp: function (e: any) {
- if (e.which !== 1 && e.button !== 0 && e.type !== "touchend") {
- return;
- }
-
- this._finish();
-
- if (!this._moved) {
- return;
- }
- // Postpone to next JS tick so internal click event handling
- // still see it as "moved".
- window.setTimeout(Util.bind(this._resetState, this), 0);
- var bounds = new LatLngBounds(this._map.containerPointToLatLng(this._startPoint), this._map.containerPointToLatLng(this._point));
-
- this._map.fire("selectionend", { selectionBounds: bounds });
- },
-
- _onKeyDown: function (e: any) {
- if (e.keyCode === 27) {
- this._finish();
- this._clearDeferredResetState();
- this._resetState();
- }
- },
});
diff --git a/frontend/react/src/map/map.ts b/frontend/react/src/map/map.ts
index dd4cc8a3..b9384789 100644
--- a/frontend/react/src/map/map.ts
+++ b/frontend/react/src/map/map.ts
@@ -43,7 +43,6 @@ import { ExplosionMarker } from "./markers/explosionmarker";
import { TextMarker } from "./markers/textmarker";
import { TargetMarker } from "./markers/targetmarker";
import {
- AirbaseSelectedEvent,
AppStateChangedEvent,
CoalitionAreaSelectedEvent,
ConfigLoadedEvent,
@@ -114,6 +113,7 @@ export class Map extends L.Map {
#lastMouseCoordinates: L.LatLng = new L.LatLng(0, 0);
#previousZoom: number = 0;
#keepRelativePositions: boolean = false;
+ #enableSelection: boolean = false;
/* Camera control plugin */
#slaveDCSCamera: boolean = false;
@@ -360,6 +360,14 @@ export class Map extends L.Map {
shiftKey: false,
ctrlKey: false,
})
+ .addShortcut("toggleEnableSelection", {
+ label: "Toggle box selection",
+ keyUpCallback: () => this.setEnableSelection(false),
+ keyDownCallback: () => this.setEnableSelection(true),
+ code: "ShiftLeft",
+ altKey: false,
+ ctrlKey: false,
+ })
.addShortcut("increaseCameraZoom", {
label: "Increase camera zoom",
keyUpCallback: () => this.increaseCameraZoom(),
@@ -725,6 +733,14 @@ export class Map extends L.Map {
return this.#keepRelativePositions;
}
+ setEnableSelection(enableSelection: boolean) {
+ this.#enableSelection = enableSelection;
+ }
+
+ getEnableSelection() {
+ return this.#enableSelection;
+ }
+
increaseCameraZoom() {
//const slider = document.querySelector(`label[title="${DCS_LINK_RATIO}"] input`);
//if (slider instanceof HTMLInputElement) {
@@ -769,7 +785,6 @@ export class Map extends L.Map {
this.#currentEffectMarker = null;
if (state !== OlympusState.UNIT_CONTROL) getApp().getUnitsManager().deselectAllUnits();
if (state !== OlympusState.DRAW || (state === OlympusState.DRAW && subState !== DrawSubState.EDIT)) this.deselectAllCoalitionAreas();
- AirbaseSelectedEvent.dispatch(null);
/* Operations to perform when entering a state */
if (state === OlympusState.IDLE) {
diff --git a/frontend/react/src/mission/airbase.ts b/frontend/react/src/mission/airbase.ts
index dd3d1ebe..894b1032 100644
--- a/frontend/react/src/mission/airbase.ts
+++ b/frontend/react/src/mission/airbase.ts
@@ -1,4 +1,4 @@
-import { DivIcon } from "leaflet";
+import { DivIcon, DomEvent } from "leaflet";
import { CustomMarker } from "../map/markers/custommarker";
import { SVGInjector } from "@tanem/svg-injector";
import { AirbaseChartData, AirbaseOptions } from "../interfaces";
@@ -29,20 +29,28 @@ export class Airbase extends CustomMarker {
AppStateChangedEvent.on((state, subState) => {
const element = this.getElement();
- if (element)
- element.style.pointerEvents = (state === OlympusState.IDLE || state === OlympusState.AIRBASE)? "all": "none";
- })
+ if (element) element.style.pointerEvents = state === OlympusState.IDLE || state === OlympusState.AIRBASE ? "all" : "none";
+ });
AirbaseSelectedEvent.on((airbase) => {
- this.#selected = airbase == this;
+ this.#selected = (airbase === this);
if (this.getElement()?.querySelector(".airbase-icon"))
(this.getElement()?.querySelector(".airbase-icon") as HTMLElement).dataset.selected = `${this.#selected}`;
- })
+ });
- this.addEventListener("click", (ev) => {
+ this.addEventListener("mousedown", (ev) => {
if (getApp().getState() === OlympusState.IDLE || getApp().getState() === OlympusState.AIRBASE) {
- getApp().setState(OlympusState.AIRBASE)
- AirbaseSelectedEvent.dispatch(this)
+ DomEvent.stop(ev);
+ ev.originalEvent.stopImmediatePropagation();
+ }
+ });
+
+ this.addEventListener("mouseup", (ev) => {
+ if (getApp().getState() === OlympusState.IDLE || getApp().getState() === OlympusState.AIRBASE) {
+ DomEvent.stop(ev);
+ ev.originalEvent.stopImmediatePropagation();
+ getApp().setState(OlympusState.AIRBASE);
+ AirbaseSelectedEvent.dispatch(this);
}
});
}
@@ -110,4 +118,8 @@ export class Airbase extends CustomMarker {
getImg() {
return this.#img;
}
+
+ getSelected() {
+ return this.#selected;
+ }
}
diff --git a/frontend/react/src/mission/missionmanager.ts b/frontend/react/src/mission/missionmanager.ts
index 4d565abe..82b2b646 100644
--- a/frontend/react/src/mission/missionmanager.ts
+++ b/frontend/react/src/mission/missionmanager.ts
@@ -6,8 +6,7 @@ import { BLUE_COMMANDER, GAME_MASTER, NONE, RED_COMMANDER } from "../constants/c
import { AirbasesData, BullseyesData, CommandModeOptions, DateAndTime, MissionData } from "../interfaces";
import { Coalition } from "../types/types";
import { Carrier } from "./carrier";
-import { NavyUnit } from "../unit/unit";
-import { BullseyesDataChanged, CommandModeOptionsChangedEvent, InfoPopupEvent } from "../events";
+import { AirbaseSelectedEvent, AppStateChangedEvent, BullseyesDataChanged, CommandModeOptionsChangedEvent, InfoPopupEvent } from "../events";
/** The MissionManager */
export class MissionManager {
@@ -34,7 +33,12 @@ export class MissionManager {
#spentSpawnPoint: number = 0;
#coalitions: { red: string[]; blue: string[] } = { red: [], blue: [] };
- constructor() {}
+ constructor() {
+ AppStateChangedEvent.on((state, subState) => {
+ if (this.getSelectedAirbase() !== null) AirbaseSelectedEvent.dispatch(null);
+ })
+
+ }
/** Update location of bullseyes
*
@@ -209,6 +213,14 @@ export class MissionManager {
return this.#frameRate;
}
+ getSelectedAirbase() {
+ const airbase = Object.values(this.#airbases).find((airbase: Airbase | Carrier) => {
+ return airbase.getSelected();
+ });
+
+ return airbase ?? null;
+ }
+
#setcommandModeOptions(commandModeOptions: CommandModeOptions) {
/* Refresh all the data if we have exited the NONE state */
var requestRefresh = false;
diff --git a/frontend/react/src/shortcut/shortcut.ts b/frontend/react/src/shortcut/shortcut.ts
index 406b097b..8f915653 100644
--- a/frontend/react/src/shortcut/shortcut.ts
+++ b/frontend/react/src/shortcut/shortcut.ts
@@ -1,4 +1,5 @@
-import { AppStateChangedEvent, ShortcutChangedEvent, ShortcutsChangedEvent } from "../events";
+import { OlympusState } from "../constants/constants";
+import { AppStateChangedEvent, ModalEvent, ShortcutChangedEvent, ShortcutsChangedEvent } from "../events";
import { ShortcutOptions } from "../interfaces";
import { keyEventWasInInput } from "../other/utils";
@@ -6,34 +7,40 @@ export class Shortcut {
#id: string;
#options: ShortcutOptions;
#keydown: boolean = false;
+ #modal: boolean = false;
constructor(id, options: ShortcutOptions) {
this.#id = id;
this.#options = options;
- AppStateChangedEvent.on(() => (this.#keydown = false));
+ AppStateChangedEvent.on((state, subState) => (this.#keydown = false));
+ ModalEvent.on((modal) => (this.#modal = modal))
/* On keyup, it is enough to check the code only, not the entire combination */
document.addEventListener("keyup", (ev: any) => {
- if (this.#keydown && options.code === ev.code) {
+ if (this.#modal) return;
+ if (this.#keydown && this.getOptions().code === ev.code) {
+ console.log(`Keydown up for shortcut ${this.#id}`)
ev.preventDefault();
- options.keyUpCallback(ev);
this.#keydown = false;
+ this.getOptions().keyUpCallback(ev);
}
});
/* On keydown, check exactly if the requested key combination is being pressed */
document.addEventListener("keydown", (ev: any) => {
+ if (this.#modal) return;
if (
- !(this.#keydown || keyEventWasInInput(ev) || options.code !== ev.code) &&
- (options.altKey === undefined || ev.altKey === (options.altKey ?? ev.code.indexOf("Alt") >= 0)) &&
- (options.ctrlKey === undefined || ev.ctrlKey === (options.ctrlKey ?? ev.code.indexOf("Control") >= 0)) &&
- (options.shiftKey === undefined || ev.shiftKey === (options.shiftKey ?? ev.code.indexOf("Shift") >= 0))
+ !(this.#keydown || keyEventWasInInput(ev) || this.getOptions().code !== ev.code) &&
+ (this.getOptions().altKey === undefined || ev.altKey === (this.getOptions().altKey ?? ev.code.indexOf("Alt") >= 0)) &&
+ (this.getOptions().ctrlKey === undefined || ev.ctrlKey === (this.getOptions().ctrlKey ?? ev.code.indexOf("Control") >= 0)) &&
+ (this.getOptions().shiftKey === undefined || ev.shiftKey === (this.getOptions().shiftKey ?? ev.code.indexOf("Shift") >= 0))
) {
+ console.log(`Keydown event for shortcut ${this.#id}`)
ev.preventDefault();
this.#keydown = true;
-
- if (options.keyDownCallback) options.keyDownCallback(ev); /* Key down event is optional */
+ const keyDownCallback = this.getOptions().keyDownCallback
+ if (keyDownCallback) keyDownCallback(ev); /* Key down event is optional */
}
});
}
@@ -43,7 +50,7 @@ export class Shortcut {
}
setOptions(options: ShortcutOptions) {
- this.#options = { ...options };
+ this.#options = { ...this.#options, ...options };
}
getId() {
@@ -54,14 +61,17 @@ export class Shortcut {
let actions: string[] = [];
if (this.getOptions().shiftKey) actions.push("Shift");
if (this.getOptions().altKey) actions.push("Alt");
- if (this.getOptions().ctrlKey) actions.push("Ctrl")
- actions.push(this.getOptions().code.replace("Key", "")
- .replace("ControlLeft", "Left Ctrl")
- .replace("AltLeft", "Left Alt")
- .replace("ShiftLeft", "Left Shift")
- .replace("ControlRight", "Right Ctrl")
- .replace("AltRight", "Right Alt")
- .replace("ShiftRight", "Right Shift"))
- return actions
+ if (this.getOptions().ctrlKey) actions.push("Ctrl");
+ actions.push(
+ this.getOptions()
+ .code.replace("Key", "")
+ .replace("ControlLeft", "Left Ctrl")
+ .replace("AltLeft", "Left Alt")
+ .replace("ShiftLeft", "Left Shift")
+ .replace("ControlRight", "Right Ctrl")
+ .replace("AltRight", "Right Alt")
+ .replace("ShiftRight", "Right Shift")
+ );
+ return actions;
}
}
diff --git a/frontend/react/src/shortcut/shortcutmanager.ts b/frontend/react/src/shortcut/shortcutmanager.ts
index e5ad2af2..3cdb7de6 100644
--- a/frontend/react/src/shortcut/shortcutmanager.ts
+++ b/frontend/react/src/shortcut/shortcutmanager.ts
@@ -1,4 +1,4 @@
-import { ShortcutChangedEvent, ShortcutsChangedEvent } from "../events";
+import { ShortcutsChangedEvent } from "../events";
import { ShortcutOptions } from "../interfaces";
import { Shortcut } from "./shortcut";
diff --git a/frontend/react/src/ui/modals/components/modal.tsx b/frontend/react/src/ui/modals/components/modal.tsx
index 9b0eb9bc..1592944a 100644
--- a/frontend/react/src/ui/modals/components/modal.tsx
+++ b/frontend/react/src/ui/modals/components/modal.tsx
@@ -1,15 +1,28 @@
-import React from "react";
+import React, { useEffect } from "react";
+import { ModalEvent } from "../../../events";
+
+export function Modal(props: { open: boolean; children?: JSX.Element | JSX.Element[]; className?: string }) {
+ useEffect(() => {
+ ModalEvent.dispatch(props.open);
+ }, [props.open]);
-export function Modal(props: { grayout?: boolean; children?: JSX.Element | JSX.Element[]; className?: string }) {
return (
-
- {props.children}
-
+ <>
+ {props.open && (
+ <>
+
+
+ {props.children}
+
+ >
+ )}
+ >
);
}
diff --git a/frontend/react/src/ui/modals/keybindmodal.tsx b/frontend/react/src/ui/modals/keybindmodal.tsx
index a2e1146e..47a22852 100644
--- a/frontend/react/src/ui/modals/keybindmodal.tsx
+++ b/frontend/react/src/ui/modals/keybindmodal.tsx
@@ -6,7 +6,6 @@ import { getApp } from "../../olympusapp";
import { OlympusState } from "../../constants/constants";
import { Shortcut } from "../../shortcut/shortcut";
import { BindShortcutRequestEvent, ShortcutsChangedEvent } from "../../events";
-import { OlToggle } from "../components/oltoggle";
export function KeybindModal(props: { open: boolean }) {
const [shortcuts, setShortcuts] = useState({} as { [key: string]: Shortcut });
@@ -23,180 +22,139 @@ export function KeybindModal(props: { open: boolean }) {
document.addEventListener("keydown", (ev) => {
if (ev.code) {
setCode(ev.code);
+ if (ev.code.indexOf("Control") < 0) setCtrlKey(ev.ctrlKey);
+ else setCtrlKey(false);
+ if (ev.code.indexOf("Shift") < 0) setShiftKey(ev.shiftKey);
+ else setShiftKey(false);
+ if (ev.code.indexOf("Alt") < 0) setAltKey(ev.altKey);
+ else setAltKey(false);
}
});
}, []);
useEffect(() => {
- setCode(shortcut?.getOptions().code ?? null)
- setShiftKey(shortcut?.getOptions().shiftKey)
- setAltKey(shortcut?.getOptions().altKey)
- setCtrlKey(shortcut?.getOptions().ctrlKey)
- }, [shortcut])
+ setCode(shortcut?.getOptions().code ?? null);
+ setShiftKey(shortcut?.getOptions().shiftKey);
+ setAltKey(shortcut?.getOptions().altKey);
+ setCtrlKey(shortcut?.getOptions().ctrlKey);
+ }, [shortcut]);
let available: null | boolean = code ? true : null;
- let inUseShortcut: null | Shortcut = null;
+ let inUseShortcuts: Shortcut[] = [];
for (let id in shortcuts) {
- if (id !== shortcut?.getId() &&
+ if (
+ id !== shortcut?.getId() &&
code === shortcuts[id].getOptions().code &&
((shiftKey === undefined && shortcuts[id].getOptions().shiftKey !== undefined) ||
(shiftKey !== undefined && shortcuts[id].getOptions().shiftKey === undefined) ||
- shiftKey === shortcuts[id].getOptions().shiftKey) && (
- (altKey === undefined && shortcuts[id].getOptions().altKey !== undefined) ||
+ shiftKey === shortcuts[id].getOptions().shiftKey) &&
+ ((altKey === undefined && shortcuts[id].getOptions().altKey !== undefined) ||
(altKey !== undefined && shortcuts[id].getOptions().altKey === undefined) ||
- altKey === shortcuts[id].getOptions().altKey) && (
- (ctrlKey === undefined && shortcuts[id].getOptions().ctrlKey !== undefined) ||
+ altKey === shortcuts[id].getOptions().altKey) &&
+ ((ctrlKey === undefined && shortcuts[id].getOptions().ctrlKey !== undefined) ||
(ctrlKey !== undefined && shortcuts[id].getOptions().ctrlKey === undefined) ||
ctrlKey === shortcuts[id].getOptions().ctrlKey)
) {
available = false;
- inUseShortcut = shortcuts[id];
+ inUseShortcuts.push(shortcuts[id]);
}
}
return (
- <>
- {props.open && (
- <>
-
+
+
+
-
-
-
- {shortcut?.getOptions().label}
-
-
- Press the key you want to bind to this event
-
-
-
{code}
-
-
-
{
- if (shiftKey === false) setShiftKey(undefined);
- else if (shiftKey === undefined) setShiftKey(true);
- else setShiftKey(false);
- }}
- toggled={shiftKey}
- >
-
- {shiftKey === true && "Shift key must be pressed"}
- {shiftKey === undefined && "Shift key can be anything"}
- {shiftKey === false && "Shift key must NOT be pressed"}
-
-
-
-
{
- if (altKey === false) setAltKey(undefined);
- else if (altKey === undefined) setAltKey(true);
- else setAltKey(false);
- }}
- toggled={altKey}
- >
-
- {altKey === true && "Alt key must be pressed"}
- {altKey === undefined && "Alt key can be anything"}
- {altKey === false && "Alt key must NOT be pressed"}
-
-
-
-
{
- if (ctrlKey === false) setCtrlKey(undefined);
- else if (ctrlKey === undefined) setCtrlKey(true);
- else setCtrlKey(false);
- }}
- toggled={ctrlKey}
- >
-
- {ctrlKey === true && "Ctrl key must be pressed"}
- {ctrlKey === undefined && "Ctrl key can be anything"}
- {ctrlKey === false && "Ctrl key must NOT be pressed"}
-
-
-
-
-
- {available === true &&
Keybind is free!
}
- {available === false && (
-
- Keybind is already in use: {inUseShortcut?.getOptions().label}
-
- )}
-
-
-
- {available && shortcut && (
- {
- if (shortcut && code) {
- let options = shortcut.getOptions();
- options.code = code;
- options.altKey = altKey;
- options.shiftKey = shiftKey;
- options.ctrlKey = ctrlKey;
- getApp().getShortcutManager().setShortcutOption(shortcut.getId(), options);
-
- getApp().setState(OlympusState.OPTIONS);
- }
- }}
- className={`
- mb-2 me-2 ml-auto flex content-center items-center gap-2
- rounded-sm bg-blue-700 px-5 py-2.5 text-sm font-medium
- text-white
- dark:bg-blue-600 dark:hover:bg-blue-700
- dark:focus:ring-blue-800
- focus:outline-none focus:ring-4 focus:ring-blue-300
- hover:bg-blue-800
- `}
- >
- Continue
-
-
- )}
- getApp().setState(OlympusState.OPTIONS)}
- className={`
- mb-2 me-2 flex content-center items-center gap-2 rounded-sm
- border-[1px] bg-blue-700 px-5 py-2.5 text-sm font-medium
- text-white
- dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400
- dark:hover:bg-gray-700 dark:focus:ring-blue-800
- focus:outline-none focus:ring-4 focus:ring-blue-300
- hover:bg-blue-800
- `}
- >
- Back
-
+ {shortcut?.getOptions().label}
+
+
+ Press the key you want to bind to this event
+
+
+
+ {ctrlKey && "Ctrl + "}
+ {altKey && "Alt + "}
+ {shiftKey && "Shift + "}
+ {code}
+
+
+ {available === true &&
Keybind is free!
}
+ {available === false && (
+
+
+ Keybind is already in use:
{inUseShortcuts.map((shortcut) => {shortcut.getOptions().label} )}
+
A key combination can be assigned to multiple actions, and all bound actions will fire
-
-
- >
- )}
- >
+ )}
+
+
+
+ {shortcut && (
+ {
+ if (shortcut && code) {
+ let options = shortcut.getOptions();
+ options.code = code;
+ options.altKey = altKey;
+ options.shiftKey = shiftKey;
+ options.ctrlKey = ctrlKey;
+ getApp().getShortcutManager().setShortcutOption(shortcut.getId(), options);
+
+ getApp().setState(OlympusState.OPTIONS);
+ }
+ }}
+ className={`
+ mb-2 me-2 ml-auto flex content-center items-center gap-2
+ rounded-sm bg-blue-700 px-5 py-2.5 text-sm font-medium
+ text-white
+ dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800
+ focus:outline-none focus:ring-4 focus:ring-blue-300
+ hover:bg-blue-800
+ `}
+ >
+ Continue
+
+
+ )}
+ getApp().setState(OlympusState.OPTIONS)}
+ className={`
+ mb-2 me-2 flex content-center items-center gap-2 rounded-sm
+ border-[1px] bg-blue-700 px-5 py-2.5 text-sm font-medium
+ text-white
+ dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400
+ dark:hover:bg-gray-700 dark:focus:ring-blue-800
+ focus:outline-none focus:ring-4 focus:ring-blue-300
+ hover:bg-blue-800
+ `}
+ >
+ Back
+
+
+
+
);
}
diff --git a/frontend/react/src/ui/modals/login.tsx b/frontend/react/src/ui/modals/loginmodal.tsx
similarity index 96%
rename from frontend/react/src/ui/modals/login.tsx
rename to frontend/react/src/ui/modals/loginmodal.tsx
index 99c0f4a6..c7d0aa8c 100644
--- a/frontend/react/src/ui/modals/login.tsx
+++ b/frontend/react/src/ui/modals/loginmodal.tsx
@@ -1,26 +1,25 @@
import React, { useEffect, useState } from "react";
import { Modal } from "./components/modal";
import { Card } from "./components/card";
-import { ErrorCallout } from "../../ui/components/olcallout";
+import { ErrorCallout } from "../components/olcallout";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faArrowRight, faCheckCircle, faExternalLink } from "@fortawesome/free-solid-svg-icons";
import { getApp, VERSION } from "../../olympusapp";
import { sha256 } from "js-sha256";
import { BLUE_COMMANDER, GAME_MASTER, OlympusState, RED_COMMANDER } from "../../constants/constants";
-import { FaTrash, FaXmark } from "react-icons/fa6";
-export function LoginModal(props: {}) {
+export function LoginModal(props: { open: boolean }) {
// TODO: add warning if not in secure context and some features are disabled
const [password, setPassword] = useState("");
- const [profileName, setProfileName] = useState("Game Master");
+ const [profileName, setProfileName] = useState("");
const [checkingPassword, setCheckingPassword] = useState(false);
const [loginError, setLoginError] = useState(false);
const [commandMode, setCommandMode] = useState(null as null | string);
useEffect(() => {
/* Set the profile name */
- getApp().setProfile(profileName);
- }, [profileName])
+ if (profileName !== "") getApp().setProfile(profileName);
+ }, [profileName]);
function checkPassword(password: string) {
setCheckingPassword(true);
@@ -51,14 +50,14 @@ export function LoginModal(props: {}) {
getApp().setState(OlympusState.IDLE);
/* If no profile exists already with that name, create it from scratch from the defaults */
- if (getApp().getProfile() === null)
- getApp().saveProfile();
+ if (getApp().getProfile() === null) getApp().saveProfile();
/* Load the profile */
getApp().loadProfile();
}
return (
-
- The profile name you choose determines the saved key binds, groups and options you see.
-
+
The profile name you choose determines the saved key binds, groups and options you see.
- {props.open && (
- <>
-
-
-
-
- Your selection contains protected units, are you sure you want to continue?
-
-
- Pressing "Continue" will cause all DCS controlled units in the current selection to abort their mission and start following Olympus commands
- only.
-
-
- If you are trying to delete a human player unit, they will be killed and de-slotted. Be careful!
-
-
- To disable this warning, press on the{" "}
-
-
- {" "}
- button
-
-
-
- {
- getApp().getUnitsManager().executeProtectionCallback();
- getApp().setState(OlympusState.UNIT_CONTROL);
- }}
- className={`
- mb-2 me-2 ml-auto flex content-center items-center gap-2
- rounded-sm bg-blue-700 px-5 py-2.5 text-sm font-medium
- text-white
- dark:bg-blue-600 dark:hover:bg-blue-700
- dark:focus:ring-blue-800
- focus:outline-none focus:ring-4 focus:ring-blue-300
- hover:bg-blue-800
- `}
- >
- Continue
-
-
- getApp().setState(OlympusState.UNIT_CONTROL)}
- className={`
- mb-2 me-2 flex content-center items-center gap-2 rounded-sm
- border-[1px] bg-blue-700 px-5 py-2.5 text-sm font-medium
- text-white
- dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400
- dark:hover:bg-gray-700 dark:focus:ring-blue-800
- focus:outline-none focus:ring-4 focus:ring-blue-300
- hover:bg-blue-800
- `}
- >
- Back
-
-
-
-
-
- >
- )}
- >
- );
-}
diff --git a/frontend/react/src/ui/modals/protectionpromptmodal.tsx b/frontend/react/src/ui/modals/protectionpromptmodal.tsx
new file mode 100644
index 00000000..d8d0d230
--- /dev/null
+++ b/frontend/react/src/ui/modals/protectionpromptmodal.tsx
@@ -0,0 +1,101 @@
+import React from "react";
+import { Modal } from "./components/modal";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faArrowRight } from "@fortawesome/free-solid-svg-icons";
+import { FaLock } from "react-icons/fa6";
+import { getApp } from "../../olympusapp";
+import { OlympusState } from "../../constants/constants";
+
+export function ProtectionPromptModal(props: { open: boolean }) {
+ return (
+
+
+
+
+ Your selection contains protected units, are you sure you want to continue?
+
+
+ Pressing "Continue" will cause all DCS controlled units in the current selection to abort their mission and start following Olympus commands only.
+
+
+ If you are trying to delete a human player unit, they will be killed and de-slotted. Be careful!
+
+
+ To disable this warning, press on the{" "}
+
+
+ {" "}
+ button
+
+
+
+ {
+ getApp().getUnitsManager().executeProtectionCallback();
+ getApp().setState(OlympusState.UNIT_CONTROL);
+ }}
+ className={`
+ mb-2 me-2 ml-auto flex content-center items-center gap-2
+ rounded-sm bg-blue-700 px-5 py-2.5 text-sm font-medium text-white
+ dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800
+ focus:outline-none focus:ring-4 focus:ring-blue-300
+ hover:bg-blue-800
+ `}
+ >
+ Continue
+
+
+ getApp().setState(OlympusState.UNIT_CONTROL)}
+ className={`
+ mb-2 me-2 flex content-center items-center gap-2 rounded-sm
+ border-[1px] bg-blue-700 px-5 py-2.5 text-sm font-medium
+ text-white
+ dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400
+ dark:hover:bg-gray-700 dark:focus:ring-blue-800
+ focus:outline-none focus:ring-4 focus:ring-blue-300
+ hover:bg-blue-800
+ `}
+ >
+ Back
+
+
+
+
+ );
+}
diff --git a/frontend/react/src/ui/panels/airbasemenu.tsx b/frontend/react/src/ui/panels/airbasemenu.tsx
index 70d93de3..923aa04e 100644
--- a/frontend/react/src/ui/panels/airbasemenu.tsx
+++ b/frontend/react/src/ui/panels/airbasemenu.tsx
@@ -114,7 +114,7 @@ export function AirbaseMenu(props: { open: boolean; onClose: () => void; childre
{airbase?.getChartData().runways.map((runway, idx) => {
return (
- <>
+
{Object.keys(runway.headings[0]).map((runwayName) => {
return (
void; childre
);
})}
- >
+
);
})}
diff --git a/frontend/react/src/ui/panels/audiomenu.tsx b/frontend/react/src/ui/panels/audiomenu.tsx
index 66a83330..49729a21 100644
--- a/frontend/react/src/ui/panels/audiomenu.tsx
+++ b/frontend/react/src/ui/panels/audiomenu.tsx
@@ -19,7 +19,7 @@ export function AudioMenu(props: { open: boolean; onClose: () => void; children?
const [audioManagerEnabled, setAudioManagerEnabled] = useState(false);
const [activeSource, setActiveSource] = useState(null as AudioSource | null);
const [count, setCount] = useState(0);
- const [shortcuts, setShortcuts] = useState({})
+ const [shortcuts, setShortcuts] = useState({});
/* Preallocate 128 references for the source and sink panels. If the number of references changes, React will give an error */
const sourceRefs = Array(128)
@@ -251,20 +251,19 @@ export function AudioMenu(props: { open: boolean; onClose: () => void; children?
right: (end as HTMLDivElement).offsetLeft + (end as HTMLDivElement).clientWidth,
};
return (
- <>
-
- >
+
);
}
})
@@ -281,29 +280,26 @@ export function AudioMenu(props: { open: boolean; onClose: () => void; children?
right: (div as HTMLDivElement).offsetLeft + (div as HTMLDivElement).clientWidth,
};
return (
- <>
-
-
{
- activeSource !== sources[idx] ? setActiveSource(sources[idx]) : setActiveSource(null);
- }}
- >
-
-
-
- >
+ {
+ activeSource !== sources[idx] ? setActiveSource(sources[idx]) : setActiveSource(null);
+ }}
+ >
+
+
);
}
})}
@@ -318,28 +314,25 @@ export function AudioMenu(props: { open: boolean; onClose: () => void; children?
right: (div as HTMLDivElement).offsetLeft + (div as HTMLDivElement).clientWidth,
};
return (
- <>
-
-
{
- if (activeSource.getConnectedTo().includes(sinks[idx])) activeSource.disconnect(sinks[idx]);
- else activeSource.connect(sinks[idx]);
- }}
- >
- {" "}
- {activeSource.getConnectedTo().includes(sinks[idx]) ? : }
-
-
- >
+ {
+ if (activeSource.getConnectedTo().includes(sinks[idx])) activeSource.disconnect(sinks[idx]);
+ else activeSource.connect(sinks[idx]);
+ }}
+ >
+ {" "}
+ {activeSource.getConnectedTo().includes(sinks[idx]) ? : }
+
);
}
})}
diff --git a/frontend/react/src/ui/panels/controlspanel.tsx b/frontend/react/src/ui/panels/controlspanel.tsx
index 9488daeb..fda86a97 100644
--- a/frontend/react/src/ui/panels/controlspanel.tsx
+++ b/frontend/react/src/ui/panels/controlspanel.tsx
@@ -46,21 +46,14 @@ export function ControlsPanel(props: {}) {
text: "Select unit",
},
{
- actions: [touch ? faHandPointer : "LMB", "Drag"],
+ actions: ["Shift", "LMB", "Drag"],
text: "Box selection",
},
{
- actions: [touch ? faHandPointer : "Wheel", "Drag"],
+ actions: [touch ? faHandPointer : "LMB", "Drag"],
text: "Move map",
},
];
- if (!touch) {
- controls.push({
- actions: ["Shift", "LMB", "Drag"],
-
- text: "Box selection",
- });
- }
if (appState === OlympusState.IDLE) {
controls = baseControls;
diff --git a/frontend/react/src/ui/panels/gamemastermenu.tsx b/frontend/react/src/ui/panels/gamemastermenu.tsx
index e14f6bdb..49a22f60 100644
--- a/frontend/react/src/ui/panels/gamemastermenu.tsx
+++ b/frontend/react/src/ui/panels/gamemastermenu.tsx
@@ -38,20 +38,12 @@ export function GameMasterMenu(props: { open: boolean; onClose: () => void; chil
GAME MASTER
)}
- {commandModeOptions.commandMode === BLUE_COMMANDER && (
-
- BLUE COMMANDER
-
- )}
- {commandModeOptions.commandMode === RED_COMMANDER && (
-
- RED COMMANDER
-
- )}
+ {commandModeOptions.commandMode === BLUE_COMMANDER &&
BLUE COMMANDER
}
+ {commandModeOptions.commandMode === RED_COMMANDER &&
RED COMMANDER
}
{serverStatus.elapsedTime > currentSetupTime && (
void; chil
.map((era) => {
return (
void; chil
group flex flex-row rounded-md justify-content gap-4 px-4 py-2
`}
>
- Elapsed time (seconds) {serverStatus.elapsedTime?.toFixed()}
+ Elapsed time (seconds) {" "}
+
+ {serverStatus.elapsedTime?.toFixed()}
+
{commandModeOptions.commandMode === GAME_MASTER && (
{
diff --git a/frontend/react/src/ui/panels/infobar.tsx b/frontend/react/src/ui/panels/infobar.tsx
index 98d40f68..1e5f1050 100644
--- a/frontend/react/src/ui/panels/infobar.tsx
+++ b/frontend/react/src/ui/panels/infobar.tsx
@@ -15,12 +15,11 @@ export function InfoBar(props: {}) {
}, []);
return (
-
+
{messages.slice(Math.max(0, messages.length - 4), Math.max(0, messages.length)).map((message, idx) => {
return (
void; childre
`}
>
setOpenAccordion(openAccordion === Accordion.NONE ? Accordion.BINDINGS : Accordion.NONE)}
+ onClick={() => setOpenAccordion(openAccordion !== Accordion.BINDINGS ? Accordion.BINDINGS : Accordion.NONE)}
open={openAccordion === Accordion.BINDINGS}
title="Key bindings"
>
@@ -53,9 +53,10 @@ export function OptionsMenu(props: { open: boolean; onClose: () => void; childre
.map(([id, shortcut]) => {
return (
{
@@ -65,39 +66,9 @@ export function OptionsMenu(props: { open: boolean; onClose: () => void; childre
>
{shortcut.getOptions().label}
- {shortcut.getOptions().altKey ? (
-
- ) : shortcut.getOptions().altKey === false ? (
-
- ) : (
- ""
- )}
- {shortcut.getOptions().ctrlKey ? (
-
- ) : shortcut.getOptions().ctrlKey === false ? (
-
- ) : (
- ""
- )}
- {shortcut.getOptions().shiftKey ? (
-
- ) : shortcut.getOptions().shiftKey === false ? (
-
- ) : (
- ""
- )}
+ {shortcut.getOptions().ctrlKey && "Ctrl + "}
+ {shortcut.getOptions().altKey && "Alt + "}
+ {shortcut.getOptions().shiftKey && "Shift + "}
{shortcut.getOptions().code}
@@ -107,7 +78,7 @@ export function OptionsMenu(props: { open: boolean; onClose: () => void; childre
setOpenAccordion(openAccordion === Accordion.NONE ? Accordion.MAP_OPTIONS : Accordion.NONE)}
+ onClick={() => setOpenAccordion(openAccordion !== Accordion.MAP_OPTIONS ? Accordion.MAP_OPTIONS : Accordion.NONE)}
open={openAccordion === Accordion.MAP_OPTIONS}
title="Map options"
>
@@ -158,7 +129,7 @@ export function OptionsMenu(props: { open: boolean; onClose: () => void; childre
getApp().getMap().setOption("hideUnitsShortRangeRings", !mapOptions.hideUnitsShortRangeRings)}
@@ -166,11 +137,11 @@ export function OptionsMenu(props: { open: boolean; onClose: () => void; childre
{}}>
Hide Short range Rings
-
+
getApp().getMap().setOption("hideGroupMembers", !mapOptions.hideGroupMembers)}
@@ -181,7 +152,7 @@ export function OptionsMenu(props: { open: boolean; onClose: () => void; childre
getApp().getMap().setOption("showMinimap", !mapOptions.showMinimap)}
@@ -192,7 +163,7 @@ export function OptionsMenu(props: { open: boolean; onClose: () => void; childre
setOpenAccordion(openAccordion === Accordion.NONE ? Accordion.CAMERA_PLUGIN : Accordion.NONE)}
+ onClick={() => setOpenAccordion(openAccordion !== Accordion.CAMERA_PLUGIN ? Accordion.CAMERA_PLUGIN : Accordion.NONE)}
open={openAccordion === Accordion.CAMERA_PLUGIN}
title="Camera plugin options"
>
diff --git a/frontend/react/src/ui/panels/radiossummarypanel.tsx b/frontend/react/src/ui/panels/radiossummarypanel.tsx
index 74562ddb..5c846c37 100644
--- a/frontend/react/src/ui/panels/radiossummarypanel.tsx
+++ b/frontend/react/src/ui/panels/radiossummarypanel.tsx
@@ -32,6 +32,7 @@ export function RadiosSummaryPanel(props: {}) {
.map((radioSink, idx) => {
return (
{}}
onMouseDown={() => {
@@ -41,28 +42,23 @@ export function RadiosSummaryPanel(props: {}) {
radioSink.setPtt(false);
}}
tooltip="Click to talk, lights up when receiving"
- buttonColor={
- radioSink.getReceiving()
- ? "white"
- : null
- }
+ buttonColor={radioSink.getReceiving() ? "white" : null}
>
{idx + 1}
);
})}
- {audioSinks.filter((audioSinks) => audioSinks instanceof UnitSink).length > 0 && (
-
- )}
+ {audioSinks.filter((audioSinks) => audioSinks instanceof UnitSink).length > 0 && }
{audioSinks.filter((audioSinks) => audioSinks instanceof UnitSink).length > 0 &&
audioSinks
.filter((audioSinks) => audioSinks instanceof UnitSink)
.map((unitSink, idx) => {
return (
{}}
onMouseDown={() => {
diff --git a/frontend/react/src/ui/panels/unitspawnmenu.tsx b/frontend/react/src/ui/panels/unitspawnmenu.tsx
index 979e0a13..960c017e 100644
--- a/frontend/react/src/ui/panels/unitspawnmenu.tsx
+++ b/frontend/react/src/ui/panels/unitspawnmenu.tsx
@@ -17,6 +17,7 @@ import { faStar } from "@fortawesome/free-solid-svg-icons";
import { OlStringInput } from "../components/olstringinput";
import { countryCodes } from "../data/codes";
import { OlAccordion } from "../components/olaccordion";
+import { AppStateChangedEvent } from "../../events";
export function UnitSpawnMenu(props: {
starredSpawns: { [key: string]: SpawnRequestTable };
@@ -32,6 +33,7 @@ export function UnitSpawnMenu(props: {
const altitudeStep = altitudeIncrements[props.blueprint.category];
/* State initialization */
+ const [appState, setAppState] = useState(OlympusState.NOT_INITIALIZED);
const [spawnCoalition, setSpawnCoalition] = useState("blue" as Coalition);
const [spawnNumber, setSpawnNumber] = useState(1);
const [spawnRole, setSpawnRole] = useState("");
@@ -46,9 +48,14 @@ export function UnitSpawnMenu(props: {
const [key, setKey] = useState("");
const [spawnRequestTable, setSpawnRequestTable] = useState(null as null | SpawnRequestTable);
- /* When the menu is opened show the unit preview on the map as a cursor */
useEffect(() => {
- if (!props.airbase && spawnRequestTable) {
+ setAppState(getApp()?.getState())
+ AppStateChangedEvent.on((state, subState) => setAppState(state));
+ }, []);
+
+ /* When the menu is opened show the unit preview on the map as a cursor */
+ const setSpawnRequestTableCallback = useCallback(() => {
+ if (!props.airbase && spawnRequestTable && appState === OlympusState.SPAWN) {
/* Refresh the unique key identified */
const newKey = hash(JSON.stringify(spawnRequestTable));
setKey(newKey);
@@ -56,7 +63,9 @@ export function UnitSpawnMenu(props: {
getApp()?.getMap()?.setSpawnRequestTable(spawnRequestTable);
getApp().setState(OlympusState.SPAWN, SpawnSubState.SPAWN_UNIT);
}
- }, [spawnRequestTable]);
+ }, [spawnRequestTable, appState]);
+
+ useEffect(setSpawnRequestTableCallback, [spawnRequestTable]);
/* Callback and effect to update the quick access name of the starredSpawn */
const updateStarredSpawnQuickAccessNameS = useCallback(() => {
@@ -231,6 +240,7 @@ export function UnitSpawnMenu(props: {
{roles.map((role) => {
return (
{
setSpawnRole(role);
setSpawnLoadout("");
@@ -256,6 +266,7 @@ export function UnitSpawnMenu(props: {
{loadouts.map((loadout) => {
return (
{
setSpawnLoadout(loadout.name);
}}
@@ -285,10 +296,9 @@ export function UnitSpawnMenu(props: {
>
Livery
-
+
{props.blueprint.liveries &&
Object.keys(props.blueprint.liveries)
.sort((ida, idb) => {
@@ -303,6 +313,7 @@ export function UnitSpawnMenu(props: {
});
return (
{
setSpawnLiveryID(id);
}}
@@ -315,9 +326,10 @@ export function UnitSpawnMenu(props: {
`}
>
{props.blueprint.liveries && props.blueprint.liveries[id].countries.length == 1 && (
-
+
)}
@@ -348,6 +360,7 @@ export function UnitSpawnMenu(props: {
{["Average", "Good", "High", "Excellent"].map((skill) => {
return (
{
setSpawnSkill(skill);
}}
@@ -383,7 +396,7 @@ export function UnitSpawnMenu(props: {
>
{spawnLoadout.items.map((item) => {
return (
-
+
Spawn
diff --git a/frontend/react/src/ui/ui.tsx b/frontend/react/src/ui/ui.tsx
index 0a70b9a9..392b8b13 100644
--- a/frontend/react/src/ui/ui.tsx
+++ b/frontend/react/src/ui/ui.tsx
@@ -10,7 +10,7 @@ import { OptionsMenu } from "./panels/optionsmenu";
import { MapHiddenTypes, MapOptions } from "../types/types";
import { NO_SUBSTATE, OlympusState, OlympusSubState, OptionsSubstate, SpawnSubState, UnitControlSubState } from "../constants/constants";
import { getApp, setupApp } from "../olympusapp";
-import { LoginModal } from "./modals/login";
+import { LoginModal } from "./modals/loginmodal";
import { MiniMapPanel } from "./panels/minimappanel";
import { UnitControlBar } from "./panels/unitcontrolbar";
@@ -20,7 +20,7 @@ import { MapContextMenu } from "./contextmenus/mapcontextmenu";
import { AirbaseMenu } from "./panels/airbasemenu";
import { AudioMenu } from "./panels/audiomenu";
import { FormationMenu } from "./panels/formationmenu";
-import { ProtectionPrompt } from "./modals/protectionprompt";
+import { ProtectionPromptModal } from "./modals/protectionpromptmodal";
import { KeybindModal } from "./modals/keybindmodal";
import { UnitExplosionMenu } from "./panels/unitexplosionmenu";
import { JTACMenu } from "./panels/jtacmenu";
@@ -65,16 +65,8 @@ export function UI() {
>
- {appState === OlympusState.LOGIN && (
- <>
-
-
- >
- )}
-
-
+
+