Added Pilot Filter functionality

This commit is contained in:
PxlLoewe
2025-07-15 23:47:13 -07:00
parent 46fdd2e0c2
commit fc698b22d7
7 changed files with 124 additions and 12 deletions

View File

@@ -2,10 +2,12 @@
import { useLeftMenuStore } from "_store/leftMenuStore";
import { cn } from "@repo/shared-components";
import { SettingsIcon } from "lucide-react";
import { usePilotConnectionStore } from "_store/pilot/connectionStore";
export const SettingsBoard = () => {
const { setSituationTabOpen, situationTabOpen } = useLeftMenuStore();
const { followOwnAircraft, showOtherAircrafts, showOtherMissions, setMapOptions } =
usePilotConnectionStore();
const cross = (
<svg
aria-label="disabled"
@@ -59,7 +61,11 @@ export const SettingsBoard = () => {
</h2>
<div className="flex items-center gap-2">
<label className="toggle text-base-content">
<input type="checkbox" />
<input
type="checkbox"
checked={followOwnAircraft}
onChange={(e) => setMapOptions({ followOwnAircraft: e.target.checked })}
/>
{cross}
{check}
</label>
@@ -67,7 +73,11 @@ export const SettingsBoard = () => {
</div>
<div className="flex items-center gap-2">
<label className="toggle text-base-content">
<input type="checkbox" />
<input
type="checkbox"
checked={showOtherAircrafts}
onChange={(e) => setMapOptions({ showOtherAircrafts: e.target.checked })}
/>
{cross}
{check}
</label>
@@ -75,7 +85,11 @@ export const SettingsBoard = () => {
</div>
<div className="flex items-center gap-2">
<label className="toggle text-base-content">
<input type="checkbox" />
<input
type="checkbox"
checked={showOtherMissions}
onChange={(e) => setMapOptions({ showOtherMissions: e.target.checked })}
/>
{cross}
{check}
</label>

View File

@@ -16,6 +16,7 @@ import { useQuery } from "@tanstack/react-query";
import { getConnectedAircraftPositionLogAPI, getConnectedAircraftsAPI } from "_querys/aircrafts";
import { getMissionsAPI } from "_querys/missions";
import { FMS_STATUS_COLORS, FMS_STATUS_TEXT_COLORS } from "_helpers/fmsStatusColors";
import { usePilotConnectionStore } from "_store/pilot/connectionStore";
const AircraftPopupContent = ({
aircraft,
@@ -396,10 +397,42 @@ export const AircraftLayer = () => {
queryFn: () => getConnectedAircraftsAPI(),
refetchInterval: 10_000,
});
const { setMap } = useMapStore((state) => state);
const map = useMap();
const {
connectedAircraft,
status: pilotConnectionStatus,
showOtherAircrafts,
followOwnAircraft,
} = usePilotConnectionStore((state) => state);
const filteredAircrafts = useMemo(() => {
if (!aircrafts) return [];
return aircrafts.filter((aircraft) => {
if (pilotConnectionStatus === "connected" && !showOtherAircrafts) {
return connectedAircraft?.stationId === aircraft.stationId;
}
return true;
});
}, [aircrafts, pilotConnectionStatus, connectedAircraft, showOtherAircrafts]);
const ownAircraft = useMemo(() => {
return aircrafts?.find((aircraft) => aircraft.id === connectedAircraft?.id);
}, [aircrafts, connectedAircraft]);
useEffect(() => {
if (pilotConnectionStatus === "connected" && followOwnAircraft && ownAircraft) {
if (!ownAircraft.posLat || !ownAircraft.posLng) return;
setMap({
center: [ownAircraft.posLat, ownAircraft.posLng],
zoom: map.getZoom(),
});
}
}, [pilotConnectionStatus, followOwnAircraft, ownAircraft, setMap, map]);
return (
<>
{aircrafts?.map((aircraft) => {
{filteredAircrafts?.map((aircraft) => {
return <AircraftMarker key={aircraft.id} aircraft={aircraft} />;
})}
</>

View File

@@ -17,6 +17,7 @@ import { getMissionsAPI } from "_querys/missions";
import { useDispatchConnectionStore } from "_store/dispatch/connectionStore";
import { HPGValidationRequired } from "_helpers/hpgValidationRequired";
import { getConnectedAircraftsAPI } from "_querys/aircrafts";
import { usePilotConnectionStore } from "_store/pilot/connectionStore";
export const MISSION_STATUS_COLORS: Record<MissionState | "attention", string> = {
draft: "#0092b8",
@@ -396,6 +397,11 @@ const MissionMarker = ({ mission }: { mission: Mission }) => {
export const MissionLayer = () => {
const dispatchState = useDispatchConnectionStore((s) => s);
const dispatcherConnected = dispatchState.status === "connected";
const {
status: pilotConnectionStatus,
showOtherMissions,
selectedStation,
} = usePilotConnectionStore((state) => state);
const { data: missions = [] } = useQuery({
queryKey: ["missions"],
@@ -410,9 +416,18 @@ export const MissionLayer = () => {
return missions.filter((m: Mission) => {
if (m.state === "draft" && !dispatcherConnected) return false;
if (dispatchState.hideDraftMissions && m.state === "draft") return false;
if (pilotConnectionStatus === "connected" && !showOtherMissions)
return m.missionStationIds.includes(selectedStation!.id);
return true;
});
}, [missions, dispatcherConnected, dispatchState.hideDraftMissions]);
}, [
missions,
dispatcherConnected,
dispatchState.hideDraftMissions,
pilotConnectionStatus,
showOtherMissions,
selectedStation,
]);
// IDEA: Add Marker to Map Layer / LayerGroup
return (

View File

@@ -11,6 +11,7 @@ import { getMissionsAPI } from "_querys/missions";
import { useEffect, useMemo, useState } from "react";
import { useMap } from "react-leaflet";
import { HPGValidationRequired } from "_helpers/hpgValidationRequired";
import { usePilotConnectionStore } from "_store/pilot/connectionStore";
const PopupContent = ({
aircrafts,
@@ -136,6 +137,7 @@ const PopupContent = ({
export const MarkerCluster = () => {
const map = useMap();
const dispatchState = useDispatchConnectionStore((s) => s);
const pilotState = usePilotConnectionStore((s) => s);
const dispatcherConnected = dispatchState.status === "connected";
const { data: aircrafts } = useQuery({
queryKey: ["aircrafts"],
@@ -155,9 +157,36 @@ export const MarkerCluster = () => {
return missions.filter((m: Mission) => {
if (m.state === "draft" && !dispatcherConnected) return false;
if (dispatchState.hideDraftMissions && m.state === "draft") return false;
if (
pilotState.status === "connected" &&
!pilotState.showOtherMissions &&
pilotState.selectedStation
)
return m.missionStationIds.includes(pilotState.selectedStation.id);
return true;
});
}, [missions, dispatcherConnected, dispatchState.hideDraftMissions]);
}, [
missions,
dispatcherConnected,
dispatchState.hideDraftMissions,
pilotState.selectedStation,
pilotState.showOtherMissions,
pilotState.status,
]);
const filteredAircrafts = useMemo(() => {
return aircrafts?.filter((a: ConnectedAircraft) => {
if (pilotState.status === "connected" && !pilotState.showOtherAircrafts) {
return a.stationId === pilotState.connectedAircraft?.stationId;
}
return true;
});
}, [
aircrafts,
pilotState.status,
pilotState.showOtherAircrafts,
pilotState.connectedAircraft?.stationId,
]);
// Track zoom level in state
const [zoom, setZoom] = useState(() => map.getZoom());
@@ -178,7 +207,7 @@ export const MarkerCluster = () => {
lat: number;
lng: number;
}[] = [];
aircrafts?.forEach((aircraft) => {
filteredAircrafts?.forEach((aircraft) => {
const lat = aircraft.posLat!;
const lng = aircraft.posLng!;
@@ -255,7 +284,7 @@ export const MarkerCluster = () => {
});
return clusterWithAvgPos;
}, [aircrafts, filteredMissions, zoom]);
}, [filteredAircrafts, filteredMissions, zoom]);
return (
<>

View File

@@ -1,3 +1,5 @@
import { AudioTrack, RemoteAudioTrack, RemoteTrack } from "livekit-client";
// Helper function for distortion curve generation
function createDistortionCurve(amount: number): Float32Array {
const k = typeof amount === "number" ? amount : 50;
@@ -12,10 +14,12 @@ function createDistortionCurve(amount: number): Float32Array {
return curve;
}
export const getRadioStream = (stream: MediaStream, volume: number): MediaStream | null => {
export const getRadioStream = (track: RemoteAudioTrack, volume: number): MediaStream | null => {
try {
const audioContext = new window.AudioContext();
const sourceNode = audioContext.createMediaStreamSource(stream);
const sourceNode = audioContext.createMediaStreamSource(
new MediaStream([track.mediaStreamTrack]),
);
const destinationNode = audioContext.createMediaStreamDestination();
const gainNode = audioContext.createGain();

View File

@@ -6,6 +6,7 @@ export interface MapStore {
lat: number;
lng: number;
} | null;
map: {
center: L.LatLngExpression;
zoom: number;

View File

@@ -27,6 +27,14 @@ interface ConnectionStore {
debug?: boolean,
) => Promise<void>;
disconnect: () => void;
followOwnAircraft: boolean;
showOtherAircrafts: boolean;
showOtherMissions: boolean;
setMapOptions: (options: {
followOwnAircraft?: boolean;
showOtherAircrafts?: boolean;
showOtherMissions?: boolean;
}) => void;
}
export const usePilotConnectionStore = create<ConnectionStore>((set) => ({
@@ -37,7 +45,15 @@ export const usePilotConnectionStore = create<ConnectionStore>((set) => ({
connectedAircraft: null,
activeMission: null,
debug: false,
followOwnAircraft: false,
showOtherAircrafts: false,
showOtherMissions: false,
setMapOptions(options) {
set((state) => ({
...state,
...options,
}));
},
connect: async (uid, stationId, logoffTime, station, user, debug) =>
new Promise((resolve) => {
set({