2025-01-28 09:47:44 +01:00

261 lines
10 KiB
TypeScript

import React, { useEffect, useRef, useState } from "react";
import { OlRoundStateButton, OlStateButton, OlLockStateButton } from "../components/olstatebutton";
import { faSkull, faCamera, faFlag, faVolumeHigh, faDownload, faUpload } from "@fortawesome/free-solid-svg-icons";
import { OlDropdownItem, OlDropdown } from "../components/oldropdown";
import { OlLabelToggle } from "../components/ollabeltoggle";
import { getApp, IP } from "../../olympusapp";
import {
olButtonsVisibilityAirbase,
olButtonsVisibilityAircraft,
olButtonsVisibilityDcs,
olButtonsVisibilityGroundunit,
olButtonsVisibilityGroundunitSam,
olButtonsVisibilityHelicopter,
olButtonsVisibilityHuman,
olButtonsVisibilityNavyunit,
olButtonsVisibilityOlympus,
} from "../components/olicons";
import { FaChevronLeft, FaChevronRight, FaFloppyDisk } from "react-icons/fa6";
import { CommandModeOptionsChangedEvent, ConfigLoadedEvent, HiddenTypesChangedEvent, MapOptionsChangedEvent, MapSourceChangedEvent, SessionDataChangedEvent, SessionDataSavedEvent } from "../../events";
import { BLUE_COMMANDER, COMMAND_MODE_OPTIONS_DEFAULTS, ImportExportSubstate, MAP_HIDDEN_TYPES_DEFAULTS, MAP_OPTIONS_DEFAULTS, OlympusState } from "../../constants/constants";
import { OlympusConfig } from "../../interfaces";
import { FaCheck, FaSave, FaSpinner } from "react-icons/fa";
export function Header() {
const [mapHiddenTypes, setMapHiddenTypes] = useState(MAP_HIDDEN_TYPES_DEFAULTS);
const [mapOptions, setMapOptions] = useState(MAP_OPTIONS_DEFAULTS);
const [mapSource, setMapSource] = useState("");
const [mapSources, setMapSources] = useState([] as string[]);
const [scrolledLeft, setScrolledLeft] = useState(true);
const [scrolledRight, setScrolledRight] = useState(false);
const [audioEnabled, setAudioEnabled] = useState(false);
const [commandModeOptions, setCommandModeOptions] = useState(COMMAND_MODE_OPTIONS_DEFAULTS);
const [savingSessionData, setSavingSessionData] = useState(false);
useEffect(() => {
HiddenTypesChangedEvent.on((hiddenTypes) => setMapHiddenTypes({ ...hiddenTypes }));
MapOptionsChangedEvent.on((mapOptions) => setMapOptions({ ...mapOptions }));
MapSourceChangedEvent.on((source) => setMapSource(source));
ConfigLoadedEvent.on((config: OlympusConfig) => {
// Timeout needed to make sure the map configuration has updated
window.setTimeout(() => {
var sources = Object.keys(getApp().getMap().getMirrors()).concat(getApp().getMap().getLayers());
setMapSources(sources);
}, 200);
});
CommandModeOptionsChangedEvent.on((commandModeOptions) => {
setCommandModeOptions(commandModeOptions);
});
SessionDataChangedEvent.on(() => setSavingSessionData(true));
SessionDataSavedEvent.on(() => setSavingSessionData(false));
}, []);
/* Initialize the "scroll" position of the element */
var scrollRef = useRef(null);
useEffect(() => {
if (scrollRef.current) onScroll(scrollRef.current);
});
function onScroll(el) {
const sl = el.scrollLeft;
const sr = el.scrollWidth - el.scrollLeft - el.clientWidth;
sl < 1 && !scrolledLeft && setScrolledLeft(true);
sl > 1 && scrolledLeft && setScrolledLeft(false);
sr < 1 && !scrolledRight && setScrolledRight(true);
sr > 1 && scrolledRight && setScrolledRight(false);
}
return (
<nav
className={`
z-10 flex w-full gap-4 border-gray-200 bg-gray-300 px-3 drop-shadow-md
align-center
dark:border-gray-800 dark:bg-olympus-900
`}
>
<img src="images/icon.png" className={`my-auto h-10 w-10 rounded-md p-0`}></img>
{!scrolledLeft && (
<FaChevronLeft
className={`
absolute left-14 h-full w-6 rounded-lg px-2 py-3.5 text-gray-200
dark:bg-olympus-900
`}
/>
)}
<div
className={`
my-2 flex w-full items-center gap-3 overflow-x-scroll no-scrollbar
`}
onScroll={(ev) => onScroll(ev.target)}
ref={scrollRef}
>
<div
className={`
mr-auto hidden flex-none flex-row items-center justify-start gap-2
lg:flex
`}
>
<div className="mr-2 flex flex-col items-start">
<div
className={`
pt-1 text-xs text-gray-800
dark:text-gray-400
`}
>
Connected to
</div>
<div
className={`
flex items-center justify-center gap-2 text-sm font-extrabold
text-gray-800
dark:text-gray-200
`}
>
{IP}
</div>
</div>
{savingSessionData ? <div className="text-white"><FaSpinner className={`
animate-spin text-2xl
`}/></div> : <div className={`relative text-white`}><FaFloppyDisk className={`
absolute -top-3 text-2xl
`}/><FaCheck className={`
absolute left-[9px] top-[-6px] text-2xl text-olympus-900
`}/><FaCheck className={`absolute left-3 top-0 text-green-500`}/></div>}
<OlStateButton className="ml-8" icon={faDownload} onClick={() => {getApp().setState(OlympusState.IMPORT_EXPORT, ImportExportSubstate.EXPORT)}} checked={false}/>
<OlStateButton icon={faUpload} onClick={() => {getApp().setState(OlympusState.IMPORT_EXPORT, ImportExportSubstate.IMPORT)}} checked={false}/>
</div>
{commandModeOptions.commandMode === BLUE_COMMANDER && (
<div className={`flex h-full rounded-md bg-blue-600 px-4 text-white`}>
<span className="my-auto font-bold">BLUE Commander ({commandModeOptions.spawnPoints.blue} points)</span>
</div>
)}
<div className={`flex h-fit flex-row items-center justify-start gap-1`}>
<OlLockStateButton
checked={!mapOptions.protectDCSUnits}
onClick={() => {
getApp().getMap().setOption("protectDCSUnits", !mapOptions.protectDCSUnits);
}}
tooltip="Lock/unlock protected units (from scripted mission)"
/>
<OlRoundStateButton
checked={audioEnabled}
onClick={() => {
audioEnabled ? getApp().getAudioManager().stop() : getApp().getAudioManager().start();
setAudioEnabled(!audioEnabled);
}}
tooltip="Enable/disable audio and radio backend"
icon={faVolumeHigh}
/>
</div>
<div className={`h-8 w-0 border-l-[2px] border-gray-700`}></div>
<div className={`flex h-fit flex-row items-center justify-start gap-1`}>
{Object.entries({
human: olButtonsVisibilityHuman,
olympus: olButtonsVisibilityOlympus,
dcs: olButtonsVisibilityDcs,
}).map((entry) => {
return (
<OlRoundStateButton
key={entry[0]}
onClick={() => {
getApp().getMap().setHiddenType(entry[0], !mapHiddenTypes[entry[0]]);
}}
checked={!mapHiddenTypes[entry[0]]}
icon={entry[1]}
tooltip={"Hide/show " + entry[0] + " units"}
/>
);
})}
</div>
<div className={`h-8 w-0 border-l-[2px] border-gray-700`}></div>
<div className={`flex h-fit flex-row items-center justify-start gap-1`}>
<OlRoundStateButton
onClick={() => getApp().getMap().setHiddenType("blue", !mapHiddenTypes["blue"])}
checked={!mapHiddenTypes["blue"]}
icon={faFlag}
className={"!text-blue-500"}
tooltip={"Hide/show blue units"}
/>
<OlRoundStateButton
onClick={() => getApp().getMap().setHiddenType("red", !mapHiddenTypes["red"])}
checked={!mapHiddenTypes["red"]}
icon={faFlag}
className={"!text-red-500"}
tooltip={"Hide/show red units"}
/>
<OlRoundStateButton
onClick={() => getApp().getMap().setHiddenType("neutral", !mapHiddenTypes["neutral"])}
checked={!mapHiddenTypes["neutral"]}
icon={faFlag}
className={"!text-gray-500"}
tooltip={"Hide/show neutral units"}
/>
</div>
<div className={`h-8 w-0 border-l-[2px] border-gray-700`}></div>
<div className={`flex h-fit flex-row items-center justify-start gap-1`}>
{Object.entries({
aircraft: olButtonsVisibilityAircraft,
helicopter: olButtonsVisibilityHelicopter,
"groundunit-sam": olButtonsVisibilityGroundunitSam,
groundunit: olButtonsVisibilityGroundunit,
navyunit: olButtonsVisibilityNavyunit,
airbase: olButtonsVisibilityAirbase,
dead: faSkull,
}).map((entry) => {
return (
<OlRoundStateButton
key={entry[0]}
onClick={() => {
getApp().getMap().setHiddenType(entry[0], !mapHiddenTypes[entry[0]]);
}}
checked={!mapHiddenTypes[entry[0]]}
icon={entry[1]}
tooltip={"Hide/show " + entry[0] + " units"}
/>
);
})}
</div>
<OlLabelToggle
toggled={mapOptions.cameraPluginMode === "map"}
leftLabel={"Live"}
rightLabel={"Map"}
onClick={() => {
getApp()
.getMap()
.setOption("cameraPluginMode", mapOptions.cameraPluginMode === "live" ? "map" : "live");
}}
/>
<OlStateButton
checked={mapOptions.cameraPluginEnabled}
icon={faCamera}
onClick={() => {
getApp().getMap().setOption("cameraPluginEnabled", !mapOptions.cameraPluginEnabled);
}}
tooltip="Activate/deactivate camera plugin"
/>
<OlDropdown label={mapSource} className="w-60">
{mapSources.map((source) => {
return (
<OlDropdownItem key={source} onClick={() => getApp().getMap().setLayerName(source)}>
<div className="truncate">{source}</div>
</OlDropdownItem>
);
})}
</OlDropdown>
</div>
{!scrolledRight && (
<FaChevronRight
className={`
absolute right-0 h-full w-6 rounded-lg px-2 py-3.5 text-gray-200
dark:bg-olympus-900
`}
/>
)}
</nav>
);
}