Added warning modals

This commit is contained in:
Davide Passoni 2025-01-13 15:11:49 +01:00
parent 0c391df69f
commit 9bd206a750
7 changed files with 164 additions and 12 deletions

View File

@ -301,7 +301,8 @@ export enum OlympusState {
AUDIO = "Audio",
AIRBASE = "Airbase",
GAME_MASTER = "Game master",
IMPORT_EXPORT = "Import/export"
IMPORT_EXPORT = "Import/export",
WARNING = "Warning modal"
}
export const NO_SUBSTATE = "No substate";
@ -353,6 +354,12 @@ export enum ImportExportSubstate {
EXPORT = "EXPORT"
}
export enum WarningSubstate {
NO_SUBSTATE = "No substate",
NOT_CHROME = "Not chrome",
NOT_SECURE = "Not secure"
}
export type OlympusSubState = DrawSubState | JTACSubState | SpawnSubState | OptionsSubstate | string;
@ -379,9 +386,10 @@ export const MAP_OPTIONS_DEFAULTS: MapOptions = {
cameraPluginRatio: 1,
cameraPluginEnabled: false,
cameraPluginMode: "map",
tabletMode: false,
AWACSMode: false,
AWACSCoalition: "blue",
hideChromeWarning: false,
hideSecureWarning: false
};
export const MAP_HIDDEN_TYPES_DEFAULTS = {

View File

@ -559,6 +559,9 @@ export class Map extends L.Map {
setSpawnRequestTable(spawnRequestTable: SpawnRequestTable) {
this.#spawnRequestTable = spawnRequestTable;
this.#currentSpawnMarker?.removeFrom(this);
this.#currentSpawnMarker = this.addTemporaryMarker(spawnRequestTable.unit.location, spawnRequestTable.unit.unitType, spawnRequestTable.coalition, true);
}
addStarredSpawnRequestTable(key, spawnRequestTable: SpawnRequestTable, quickAccessName: string) {
@ -693,8 +696,8 @@ export class Map extends L.Map {
return this.#miniMapLayerGroup;
}
addTemporaryMarker(latlng: L.LatLng, name: string, coalition: string, commandHash?: string) {
var marker = new TemporaryUnitMarker(latlng, name, coalition, commandHash);
addTemporaryMarker(latlng: L.LatLng, name: string, coalition: string, headingHandle: boolean, commandHash?: string) {
var marker = new TemporaryUnitMarker(latlng, name, coalition, headingHandle, commandHash);
marker.addTo(this);
this.#temporaryMarkers.push(marker);
return marker;
@ -828,7 +831,8 @@ export class Map extends L.Map {
this.#currentSpawnMarker = new TemporaryUnitMarker(
new L.LatLng(0, 0),
this.#spawnRequestTable?.unit.unitType ?? "",
this.#spawnRequestTable?.coalition ?? "neutral"
this.#spawnRequestTable?.coalition ?? "neutral",
false
);
this.#currentSpawnMarker.addTo(this);
} else if (subState === SpawnSubState.SPAWN_EFFECT) {
@ -1073,8 +1077,10 @@ export class Map extends L.Map {
MouseMovedEvent.dispatch(e.latlng, elevation);
});
if (this.#currentSpawnMarker) this.#currentSpawnMarker.setLatLng(e.latlng);
if (this.#currentEffectMarker) this.#currentEffectMarker.setLatLng(e.latlng);
if (getApp().getState() === OlympusState.SPAWN) {
if (this.#currentSpawnMarker) this.#currentSpawnMarker.setLatLng(e.latlng);
if (this.#currentEffectMarker) this.#currentEffectMarker.setLatLng(e.latlng);
}
} else {
this.#destionationWasRotated = true;
this.#destinationRotation -= e.originalEvent.movementX;
@ -1202,7 +1208,7 @@ export class Map extends L.Map {
if (this.#keepRelativePositions) {
selectedUnits.forEach((unit) => {
if (this.#contextAction?.getOptions().type === ContextActionType.MOVE || this.#contextAction === null) {
this.#destinationPreviewMarkers[unit.ID] = new TemporaryUnitMarker(new L.LatLng(0, 0), unit.getName(), unit.getCoalition());
this.#destinationPreviewMarkers[unit.ID] = new TemporaryUnitMarker(new L.LatLng(0, 0), unit.getName(), unit.getCoalition(), false);
} else if (this.#contextAction?.getTarget() === ContextActionTarget.POINT) {
this.#destinationPreviewMarkers[unit.ID] = new TargetMarker(new L.LatLng(0, 0));
}

View File

@ -9,12 +9,14 @@ export class TemporaryUnitMarker extends CustomMarker {
#coalition: string;
#commandHash: string | undefined = undefined;
#timer: number = 0;
#headingHandle: boolean;
constructor(latlng: LatLng, name: string, coalition: string, commandHash?: string) {
constructor(latlng: LatLng, name: string, coalition: string, headingHandle: boolean, commandHash?: string) {
super(latlng, { interactive: false });
this.#name = name;
this.#coalition = coalition;
this.#commandHash = commandHash;
this.#headingHandle = headingHandle;
if (commandHash !== undefined) this.setCommandHash(commandHash);
}

View File

@ -20,7 +20,7 @@ import { WeaponsManager } from "./weapon/weaponsmanager";
import { ServerManager } from "./server/servermanager";
import { AudioManager } from "./audio/audiomanager";
import { GAME_MASTER, LoginSubState, NO_SUBSTATE, OlympusState, OlympusSubState } from "./constants/constants";
import { GAME_MASTER, LoginSubState, NO_SUBSTATE, OlympusState, OlympusSubState, WarningSubstate } from "./constants/constants";
import { AppStateChangedEvent, ConfigLoadedEvent, InfoPopupEvent, MapOptionsChangedEvent, SelectedUnitsChangedEvent, ShortcutsChangedEvent } from "./events";
import { OlympusConfig } from "./interfaces";
import { SessionDataManager } from "./sessiondata";
@ -38,6 +38,7 @@ export class OlympusApp {
#state: OlympusState = OlympusState.NOT_INITIALIZED;
#subState: OlympusSubState = NO_SUBSTATE;
#infoMessages: string[] = [];
#startupWarningsShown: boolean = false;
/* Main leaflet map, extended by custom methods */
#map: Map;
@ -306,6 +307,16 @@ export class OlympusApp {
this.#state = state;
this.#subState = subState;
if (this.#state === OlympusState.IDLE && !this.#startupWarningsShown) {
window.setTimeout(() => {
const isChrome = navigator.userAgent.indexOf("Chrome") > -1;
if (!isChrome && !this.getMap().getOptions().hideChromeWarning) this.setState(OlympusState.WARNING, WarningSubstate.NOT_CHROME);
if (!isSecureContext && !this.getMap().getOptions().hideSecureWarning) this.setState(OlympusState.WARNING, WarningSubstate.NOT_SECURE);
}, 200);
this.#startupWarningsShown = true;
}
console.log(`App state set to ${state}, substate ${subState}`);
AppStateChangedEvent.dispatch(state, subState);
}

View File

@ -25,9 +25,10 @@ export type MapOptions = {
cameraPluginRatio: number;
cameraPluginEnabled: boolean;
cameraPluginMode: string;
tabletMode: boolean;
AWACSMode: boolean;
AWACSCoalition: Coalition;
hideChromeWarning: boolean;
hideSecureWarning: boolean;
};
export type MapHiddenTypes = {

View File

@ -0,0 +1,115 @@
import React, { useEffect, useState } from "react";
import { Modal } from "./components/modal";
import { FaExclamationTriangle } from "react-icons/fa";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faArrowRight } from "@fortawesome/free-solid-svg-icons";
import { getApp } from "../../olympusapp";
import { MAP_OPTIONS_DEFAULTS, NO_SUBSTATE, OlympusState, WarningSubstate } from "../../constants/constants";
import { AppStateChangedEvent, MapOptionsChangedEvent } from "../../events";
import { OlCheckbox } from "../components/olcheckbox";
export function WarningModal(props: { open: boolean }) {
const [appState, setAppState] = useState(OlympusState.NOT_INITIALIZED);
const [appSubState, setAppSubState] = useState(NO_SUBSTATE);
const [mapOptions, setMapOptions] = useState(MAP_OPTIONS_DEFAULTS);
useEffect(() => {
AppStateChangedEvent.on((appState, appSubState) => {
setAppState(appState);
setAppSubState(appSubState);
});
MapOptionsChangedEvent.on((mapOptions) => setMapOptions({ ...mapOptions }));
}, []);
let warningText;
if (appState === OlympusState.WARNING) {
switch (appSubState) {
case WarningSubstate.NOT_CHROME:
warningText = (
<div className="flex flex-col gap-2 text-gray-400">
<span>Non-Google Chrome Browser Detected.</span>
<span>
It appears you are using a browser other than Google Chrome.
</span>
<span>If you encounter any problems, we strongly suggest you use a Chrome based browser. Many features, especially advanced ones such as audio playback and capture, were developed specifically for Chrome based browsers. </span>
<div className="mt-5 flex gap-4">
<OlCheckbox
checked={mapOptions.hideChromeWarning}
onChange={() => {
getApp().getMap().setOption("hideChromeWarning", !mapOptions.hideChromeWarning);
}}
/>{" "}
<span>Don't show this warning again</span>
</div>
</div>
);
break;
case WarningSubstate.NOT_SECURE:
case WarningSubstate.NOT_CHROME:
warningText = (
<div className="flex flex-col gap-2 text-gray-400">
<span>Your connection to DCS Olympus is not secure.</span>
<span>
To protect your personal data some advanced DCS Olympus features like the camera plugin or the audio backend
have been disabled.
</span>
<span>
To solve this issue, DCS Olympus should be served using the <span className={`
italic
`}>https</span> protocol.
</span>
<span>To do so, we suggest using a dedicated server and a reverse proxy with SSL enabled.</span>
<div className="mt-5 flex gap-4">
<OlCheckbox
checked={mapOptions.hideSecureWarning}
onChange={() => {
getApp().getMap().setOption("hideSecureWarning", !mapOptions.hideSecureWarning);
}}
/>{" "}
<span>Don't show this warning again</span>
</div>
</div>
);
break;
default:
break;
}
}
return (
<Modal
open={props.open}
className={`
inline-flex h-[50%] max-h-[600px] w-[40%] max-w-[1100px] overflow-y-auto
scroll-smooth bg-white
dark:bg-olympus-800
max-md:h-full max-md:max-h-full max-md:w-full max-md:rounded-none
max-md:border-none
`}
>
<div className="flex h-full w-full flex-col p-14">
<div className="flex gap-2 text-xl text-white">
<FaExclamationTriangle className={`my-auto text-4xl text-yellow-300`} />
<div className="my-auto">Warning</div>
</div>
<div className="mt-10 text-white">{warningText}</div>
<div className="ml-auto mt-auto flex">
<button
type="button"
onClick={() => getApp().setState(OlympusState.IDLE)}
className={`
mb-2 me-2 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
<FontAwesomeIcon className={`my-auto`} icon={faArrowRight} />
</button>
</div>
</div>
</Modal>
);
}

View File

@ -8,7 +8,13 @@ import { MainMenu } from "./panels/mainmenu";
import { SideBar } from "./panels/sidebar";
import { OptionsMenu } from "./panels/optionsmenu";
import { MapHiddenTypes, MapOptions } from "../types/types";
import { NO_SUBSTATE, OlympusState, OlympusSubState, OptionsSubstate, SpawnSubState, UnitControlSubState } from "../constants/constants";
import {
NO_SUBSTATE,
OlympusState,
OlympusSubState,
OptionsSubstate,
UnitControlSubState
} from "../constants/constants";
import { getApp, setupApp } from "../olympusapp";
import { LoginModal } from "./modals/loginmodal";
@ -34,6 +40,7 @@ import { RadiosSummaryPanel } from "./panels/radiossummarypanel";
import { AWACSMenu } from "./panels/awacsmenu";
import { ServerOverlay } from "./serveroverlay";
import { ImportExportModal } from "./modals/importexportmodal";
import { WarningModal } from "./modals/warningmodal";
export type OlympusUIState = {
mainMenuVisible: boolean;
@ -83,6 +90,8 @@ export function UI() {
<ProtectionPromptModal open={appState === OlympusState.UNIT_CONTROL && appSubState == UnitControlSubState.PROTECTION} />
<KeybindModal open={appState === OlympusState.OPTIONS && appSubState === OptionsSubstate.KEYBIND} />
<ImportExportModal open={appState === OlympusState.IMPORT_EXPORT} />
<LoginModal open={appState === OlympusState.LOGIN} />
<WarningModal open={appState === OlympusState.WARNING} />
</>
)}