added MEasurement-tool, added Dispatch-name auto change
This commit is contained in:
@@ -20,7 +20,6 @@ import { ConnectionQuality } from "livekit-client";
|
|||||||
import { ROOMS } from "_data/livekitRooms";
|
import { ROOMS } from "_data/livekitRooms";
|
||||||
import { useDispatchConnectionStore } from "_store/dispatch/connectionStore";
|
import { useDispatchConnectionStore } from "_store/dispatch/connectionStore";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { dispatchSocket } from "dispatch/socket";
|
|
||||||
import { useSounds } from "_components/Audio/useSounds";
|
import { useSounds } from "_components/Audio/useSounds";
|
||||||
|
|
||||||
export const Audio = () => {
|
export const Audio = () => {
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import { Fragment, useEffect, useState } from "react";
|
|||||||
import { cn } from "_helpers/cn";
|
import { cn } from "_helpers/cn";
|
||||||
import { asPublicUser } from "@repo/db";
|
import { asPublicUser } from "@repo/db";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { getConnectedUserAPI } from "_querys/connected-user";
|
import { getConnectedDispatcherAPI } from "_querys/dispatcher";
|
||||||
|
import { getConnectedAircraftsAPI } from "_querys/aircrafts";
|
||||||
|
|
||||||
export const Chat = () => {
|
export const Chat = () => {
|
||||||
const {
|
const {
|
||||||
@@ -26,20 +27,19 @@ export const Chat = () => {
|
|||||||
const [addTabValue, setAddTabValue] = useState<string>("default");
|
const [addTabValue, setAddTabValue] = useState<string>("default");
|
||||||
const [message, setMessage] = useState<string>("");
|
const [message, setMessage] = useState<string>("");
|
||||||
|
|
||||||
const { data: connectedUser } = useQuery({
|
const { data: dispatcher } = useQuery({
|
||||||
queryKey: ["connected-users"],
|
queryKey: ["dispatcher"],
|
||||||
queryFn: async () => {
|
queryFn: () => getConnectedDispatcherAPI(),
|
||||||
const user = await getConnectedUserAPI();
|
refetchInterval: 10000,
|
||||||
return user.filter((u) => u.userId !== session.data?.user.id);
|
});
|
||||||
},
|
const { data: aircrafts } = useQuery({
|
||||||
|
queryKey: ["aircrafts"],
|
||||||
|
queryFn: () => getConnectedAircraftsAPI(),
|
||||||
refetchInterval: 10000,
|
refetchInterval: 10000,
|
||||||
refetchOnWindowFocus: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
const filteredDispatcher = dispatcher?.filter((d) => d.userId !== session.data?.user.id);
|
||||||
if (!session.data?.user.id) return;
|
const filteredAircrafts = aircrafts?.filter((a) => a.userId !== session.data?.user.id);
|
||||||
setOwnId(session.data.user.id);
|
|
||||||
}, [session, setOwnId]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("dropdown dropdown-right", chatOpen && "dropdown-open")}>
|
<div className={cn("dropdown dropdown-right", chatOpen && "dropdown-open")}>
|
||||||
@@ -75,31 +75,36 @@ export const Chat = () => {
|
|||||||
value={addTabValue}
|
value={addTabValue}
|
||||||
onChange={(e) => setAddTabValue(e.target.value)}
|
onChange={(e) => setAddTabValue(e.target.value)}
|
||||||
>
|
>
|
||||||
{!connectedUser?.length && (
|
{!filteredDispatcher?.length && !filteredAircrafts?.length && (
|
||||||
<option disabled value="default">
|
<option disabled value="default">
|
||||||
Keine Chatpartner gefunden
|
Keine Chatpartner gefunden
|
||||||
</option>
|
</option>
|
||||||
)}
|
)}
|
||||||
{connectedUser?.length && (
|
{filteredDispatcher?.length ||
|
||||||
<option disabled value="default">
|
(filteredAircrafts?.length && (
|
||||||
Chatpartner auswählen
|
<option disabled value="default">
|
||||||
</option>
|
Chatpartner auswählen
|
||||||
)}
|
</option>
|
||||||
|
))}
|
||||||
|
|
||||||
{[
|
{filteredDispatcher?.map((dispatcher) => (
|
||||||
...(connectedUser?.filter(
|
<option key={dispatcher.userId} value={dispatcher.userId}>
|
||||||
(user, idx, arr) => arr.findIndex((u) => u.userId === user.userId) === idx,
|
{dispatcher.zone} - {asPublicUser(dispatcher.publicUser).fullName}
|
||||||
) || []),
|
</option>
|
||||||
].map((user) => (
|
))}
|
||||||
<option key={user.userId} value={user.userId}>
|
{filteredAircrafts?.map((aircraft) => (
|
||||||
{asPublicUser(user.publicUser).fullName}
|
<option key={aircraft.userId} value={aircraft.userId}>
|
||||||
|
{aircraft.Station.bosCallsignShort} -{" "}
|
||||||
|
{asPublicUser(aircraft.publicUser).fullName}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
<button
|
<button
|
||||||
className="btn btn-sm btn-soft btn-primary join-item"
|
className="btn btn-sm btn-soft btn-primary join-item"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const user = connectedUser?.find((user) => user.userId === addTabValue);
|
const aircraftUser = aircrafts?.find((a) => a.userId === addTabValue);
|
||||||
|
const dispatcherUser = dispatcher?.find((d) => d.userId === addTabValue);
|
||||||
|
const user = aircraftUser || dispatcherUser;
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
addChat(addTabValue, asPublicUser(user.publicUser).fullName);
|
addChat(addTabValue, asPublicUser(user.publicUser).fullName);
|
||||||
setSelectedChat(addTabValue);
|
setSelectedChat(addTabValue);
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ import { toast } from "react-hot-toast";
|
|||||||
import { useLeftMenuStore } from "_store/leftMenuStore";
|
import { useLeftMenuStore } from "_store/leftMenuStore";
|
||||||
import { asPublicUser } from "@repo/db";
|
import { asPublicUser } from "@repo/db";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { getConnectedUserAPI } from "_querys/connected-user";
|
import { getConnectedDispatcherAPI } from "_querys/dispatcher";
|
||||||
import { sendReportAPI } from "_querys/report";
|
import { sendReportAPI } from "_querys/report";
|
||||||
|
import { getConnectedAircraftsAPI } from "_querys/aircrafts";
|
||||||
|
|
||||||
export const Report = () => {
|
export const Report = () => {
|
||||||
const { setChatOpen, setReportTabOpen, reportTabOpen, setOwnId } = useLeftMenuStore();
|
const { setChatOpen, setReportTabOpen, reportTabOpen, setOwnId } = useLeftMenuStore();
|
||||||
@@ -22,15 +23,19 @@ export const Report = () => {
|
|||||||
setOwnId(session.data.user.id);
|
setOwnId(session.data.user.id);
|
||||||
}, [session, setOwnId]);
|
}, [session, setOwnId]);
|
||||||
|
|
||||||
const { data: connectedUser } = useQuery({
|
const { data: dispatcher } = useQuery({
|
||||||
queryKey: ["connected-users"],
|
queryKey: ["dispatcher"],
|
||||||
queryFn: async () => {
|
queryFn: () => getConnectedDispatcherAPI(),
|
||||||
const user = await getConnectedUserAPI();
|
|
||||||
return user.filter((u) => u.userId !== session.data?.user.id);
|
|
||||||
},
|
|
||||||
refetchInterval: 10000,
|
refetchInterval: 10000,
|
||||||
refetchOnWindowFocus: true,
|
|
||||||
});
|
});
|
||||||
|
const { data: aircrafts } = useQuery({
|
||||||
|
queryKey: ["aircrafts"],
|
||||||
|
queryFn: () => getConnectedAircraftsAPI(),
|
||||||
|
refetchInterval: 10000,
|
||||||
|
});
|
||||||
|
|
||||||
|
const filteredDispatcher = dispatcher?.filter((d) => d.userId !== session.data?.user.id);
|
||||||
|
const filteredAircrafts = aircrafts?.filter((a) => a.userId !== session.data?.user.id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("dropdown dropdown-right", reportTabOpen && "dropdown-open")}>
|
<div className={cn("dropdown dropdown-right", reportTabOpen && "dropdown-open")}>
|
||||||
@@ -60,23 +65,27 @@ export const Report = () => {
|
|||||||
value={selectedPlayer}
|
value={selectedPlayer}
|
||||||
onChange={(e) => setSelectedPlayer(e.target.value)}
|
onChange={(e) => setSelectedPlayer(e.target.value)}
|
||||||
>
|
>
|
||||||
{!connectedUser?.length && (
|
{!filteredDispatcher?.length && !filteredAircrafts?.length && (
|
||||||
<option disabled value="default">
|
<option disabled value="default">
|
||||||
Kein Nutzer verbunden
|
Keine Nutzer gefunden
|
||||||
</option>
|
</option>
|
||||||
)}
|
)}
|
||||||
{connectedUser?.length && (
|
{filteredDispatcher?.length ||
|
||||||
<option disabled value="default">
|
(filteredAircrafts?.length && (
|
||||||
Kein Nutzer auswählen
|
<option disabled value="default">
|
||||||
|
Nutzer auswählen
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{filteredDispatcher?.map((dispatcher) => (
|
||||||
|
<option key={dispatcher.userId} value={dispatcher.userId}>
|
||||||
|
{dispatcher.zone} - {asPublicUser(dispatcher.publicUser).fullName}
|
||||||
</option>
|
</option>
|
||||||
)}
|
))}
|
||||||
{[
|
{filteredAircrafts?.map((aircraft) => (
|
||||||
...(connectedUser?.filter(
|
<option key={aircraft.userId} value={aircraft.userId}>
|
||||||
(user, idx, arr) => arr.findIndex((u) => u.userId === user.userId) === idx,
|
{aircraft.Station.bosCallsignShort} -{" "}
|
||||||
) || []),
|
{asPublicUser(aircraft.publicUser).fullName}
|
||||||
].map((user) => (
|
|
||||||
<option key={user.userId} value={user.userId}>
|
|
||||||
{asPublicUser(user.publicUser).fullName}
|
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { AircraftLayer } from "_components/map/AircraftMarker";
|
|||||||
import { MarkerCluster } from "_components/map/_components/MarkerCluster";
|
import { MarkerCluster } from "_components/map/_components/MarkerCluster";
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import { Map as TMap } from "leaflet";
|
import { Map as TMap } from "leaflet";
|
||||||
|
import { DistanceLayer } from "_components/map/Measurement";
|
||||||
|
|
||||||
const Map = () => {
|
const Map = () => {
|
||||||
const ref = useRef<TMap | null>(null);
|
const ref = useRef<TMap | null>(null);
|
||||||
@@ -49,6 +50,7 @@ const Map = () => {
|
|||||||
<MarkerCluster />
|
<MarkerCluster />
|
||||||
<MissionLayer />
|
<MissionLayer />
|
||||||
<AircraftLayer />
|
<AircraftLayer />
|
||||||
|
<DistanceLayer />
|
||||||
</MapContainer>
|
</MapContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
102
apps/dispatch/app/_components/map/Measurement.tsx
Normal file
102
apps/dispatch/app/_components/map/Measurement.tsx
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import "leaflet.polylinemeasure";
|
||||||
|
import "leaflet.polylinemeasure/Leaflet.PolylineMeasure.css";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
import { useMap } from "react-leaflet";
|
||||||
|
import L from "leaflet";
|
||||||
|
|
||||||
|
export const DistanceLayer = () => {
|
||||||
|
const map = useMap();
|
||||||
|
const added = useRef(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (added.current) return;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
(L.control as any)
|
||||||
|
.polylineMeasure({
|
||||||
|
position: "topleft",
|
||||||
|
unit: "metres", // Show imperial or metric distances. Values: 'metres', 'landmiles', 'nauticalmiles'
|
||||||
|
clearMeasurementsOnStop: true, // Clear all the measurements when the control is unselected
|
||||||
|
showBearings: true, // Whether bearings are displayed within the tooltips
|
||||||
|
bearingTextIn: "In", // language dependend label for inbound bearings
|
||||||
|
bearingTextOut: "Out", // language dependend label for outbound bearings
|
||||||
|
tooltipTextFinish: "Klicken zum <b>Beenden</b><br>",
|
||||||
|
tooltipTextDelete: "SHIFT+Click zum <b>Löschen</b>",
|
||||||
|
tooltipTextMove: "Klicken und ziehen zum <b>Verschieben</b><br>",
|
||||||
|
tooltipTextResume: "<br>CTRL+Click zum <b>Forsetzen</b>",
|
||||||
|
tooltipTextAdd: "CTRL+Click zum <b>Hinzufügen</b>",
|
||||||
|
// language dependend labels for point's tooltips
|
||||||
|
measureControlTitleOn: "Messung starten", // Title for the control going to be switched on
|
||||||
|
measureControlTitleOff: "Messung beenden", // Title for the control going to be switched off
|
||||||
|
measureControlLabel: "📏", // Label of the Measure control (maybe a unicode symbol)
|
||||||
|
measureControlClasses: ["pointer"], // Classes to apply to the Measure control
|
||||||
|
showClearControl: true, // Show a control to clear all the measurements
|
||||||
|
clearControlTitle: "Messung löschen", // Title text to show on the clear measurements control button
|
||||||
|
clearControlLabel: "×", // Label of the Clear control (maybe a unicode symbol)
|
||||||
|
clearControlClasses: ["pointer"], // Classes to apply to clear control button
|
||||||
|
unitControlClasses: ["pointer"],
|
||||||
|
showUnitControl: true, // Show a control to change the units of measurements
|
||||||
|
unitControlTitle: {
|
||||||
|
// Title texts to show on the Unit Control
|
||||||
|
text: "Einheit ändern",
|
||||||
|
kilometres: "Kilometer",
|
||||||
|
nauticalmiles: "Nautische Meilen",
|
||||||
|
},
|
||||||
|
unitControlLabel: {
|
||||||
|
// Unit symbols to show in the Unit Control and measurement labels
|
||||||
|
metres: "m",
|
||||||
|
kilometres: "km",
|
||||||
|
feet: "ft",
|
||||||
|
landmiles: "mi",
|
||||||
|
nauticalmiles: "nm",
|
||||||
|
},
|
||||||
|
tempLine: {
|
||||||
|
// Styling settings for the temporary dashed line
|
||||||
|
color: "#00f", // Dashed line color
|
||||||
|
weight: 2, // Dashed line weight
|
||||||
|
},
|
||||||
|
fixedLine: {
|
||||||
|
// Styling for the solid line
|
||||||
|
color: "#006", // Solid line color
|
||||||
|
weight: 2, // Solid line weight
|
||||||
|
},
|
||||||
|
arrow: {
|
||||||
|
// Styling of the midway arrow
|
||||||
|
color: "#000", // Color of the arrow
|
||||||
|
},
|
||||||
|
startCircle: {
|
||||||
|
// Style settings for circle marker indicating the starting point of the polyline
|
||||||
|
color: "#000000", // Color of the border of the circle
|
||||||
|
weight: 1, // Weight of the circle
|
||||||
|
fillColor: "#000000", // Fill color of the circle
|
||||||
|
fillOpacity: 1, // Fill opacity of the circle
|
||||||
|
radius: 3, // Radius of the circle
|
||||||
|
},
|
||||||
|
intermedCircle: {
|
||||||
|
// Style settings for all circle markers between startCircle and endCircle
|
||||||
|
color: "#000", // Color of the border of the circle
|
||||||
|
weight: 1, // Weight of the circle
|
||||||
|
fillColor: "#000000", // Fill color of the circle
|
||||||
|
fillOpacity: 1, // Fill opacity of the circle
|
||||||
|
radius: 3, // Radius of the circle
|
||||||
|
},
|
||||||
|
currentCircle: {
|
||||||
|
// Style settings for circle marker indicating the latest point of the polyline during drawing a line
|
||||||
|
color: "#000000", // Color of the border of the circle
|
||||||
|
weight: 1, // Weight of the circle
|
||||||
|
fillColor: "#FFFFFF", // Fill color of the circle
|
||||||
|
fillOpacity: 1, // Fill opacity of the circle
|
||||||
|
radius: 3, // Radius of the circle
|
||||||
|
},
|
||||||
|
endCircle: {
|
||||||
|
// Style settings for circle marker indicating the last point of the polyline
|
||||||
|
color: "#000", // Color of the border of the circle
|
||||||
|
weight: 1, // Weight of the circle
|
||||||
|
fillColor: "#ffffff", // Fill color of the circle
|
||||||
|
fillOpacity: 1, // Fill opacity of the circle
|
||||||
|
radius: 3, // Radius of the circle
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.addTo(map);
|
||||||
|
added.current = true;
|
||||||
|
}, [map]);
|
||||||
|
return null;
|
||||||
|
};
|
||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
SmartphoneNfc,
|
SmartphoneNfc,
|
||||||
CheckCheck,
|
CheckCheck,
|
||||||
Cross,
|
Cross,
|
||||||
|
Radio,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import {
|
import {
|
||||||
getPublicUser,
|
getPublicUser,
|
||||||
@@ -41,6 +42,7 @@ import { useDispatchConnectionStore } from "_store/dispatch/connectionStore";
|
|||||||
import { HPGValidationRequired } from "_helpers/hpgValidationRequired";
|
import { HPGValidationRequired } from "_helpers/hpgValidationRequired";
|
||||||
import { getOsmAddress } from "_querys/osm";
|
import { getOsmAddress } from "_querys/osm";
|
||||||
import { hpgStateToFMSStatus } from "_helpers/hpgStateToFmsStatus";
|
import { hpgStateToFMSStatus } from "_helpers/hpgStateToFmsStatus";
|
||||||
|
import { cn } from "_helpers/cn";
|
||||||
|
|
||||||
const Einsatzdetails = ({
|
const Einsatzdetails = ({
|
||||||
mission,
|
mission,
|
||||||
@@ -428,7 +430,7 @@ const Rettungsmittel = ({ mission }: { mission: Mission }) => {
|
|||||||
? [{ label: "Rettungsdienst", value: "RTW", type: "vehicle" as const }]
|
? [{ label: "Rettungsdienst", value: "RTW", type: "vehicle" as const }]
|
||||||
: []),
|
: []),
|
||||||
...(!mission.hpgPoliceState || mission.hpgPoliceState === "NOT_REQUESTED"
|
...(!mission.hpgPoliceState || mission.hpgPoliceState === "NOT_REQUESTED"
|
||||||
? [{ label: "POLizei", value: "POL", type: "vehicle" as const }]
|
? [{ label: "Polizei", value: "POL", type: "vehicle" as const }]
|
||||||
: []),
|
: []),
|
||||||
].sort((a, b) => {
|
].sort((a, b) => {
|
||||||
// 1. Vehicles first
|
// 1. Vehicles first
|
||||||
@@ -443,7 +445,6 @@ const Rettungsmittel = ({ mission }: { mission: Mission }) => {
|
|||||||
|
|
||||||
// 3. Otherwise, sort alphabetically by label
|
// 3. Otherwise, sort alphabetically by label
|
||||||
return a.label.localeCompare(b.label);
|
return a.label.localeCompare(b.label);
|
||||||
return 0;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -558,25 +559,17 @@ const Rettungsmittel = ({ mission }: { mission: Mission }) => {
|
|||||||
}}
|
}}
|
||||||
value={typeof selectedStation === "string" ? selectedStation : selectedStation?.id}
|
value={typeof selectedStation === "string" ? selectedStation : selectedStation?.id}
|
||||||
>
|
>
|
||||||
{allStations
|
|
||||||
?.filter((s) => !mission.missionStationIds.includes(s.id))
|
|
||||||
?.map((station) => (
|
|
||||||
<option
|
|
||||||
key={station.id}
|
|
||||||
value={station.id}
|
|
||||||
onClick={() => {
|
|
||||||
setSelectedStation(station);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{station.bosCallsign}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
<option disabled value={"default"}>
|
|
||||||
Fahrzeuge:
|
|
||||||
</option>
|
|
||||||
{stationsOptions.map((option) => (
|
{stationsOptions.map((option) => (
|
||||||
<option key={option.value} value={option.value}>
|
<option
|
||||||
|
key={option.value}
|
||||||
|
value={option.value}
|
||||||
|
className={cn(
|
||||||
|
"flex gap-2",
|
||||||
|
"isOnline" in option && option?.isOnline && "text-green-500",
|
||||||
|
)}
|
||||||
|
>
|
||||||
{option.label}
|
{option.label}
|
||||||
|
{"isOnline" in option && option?.isOnline && " (Online)"}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { PublicUser } from "@repo/db";
|
import { PublicUser } from "@repo/db";
|
||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { getConnectedAircraftsAPI, kickAircraftAPI } from "_querys/aircrafts";
|
import { getConnectedAircraftsAPI, kickAircraftAPI } from "_querys/aircrafts";
|
||||||
import { getConnectedDispatcherAPI, kickDispatcherAPI } from "_querys/connected-user";
|
import { getConnectedDispatcherAPI, kickDispatcherAPI } from "_querys/dispatcher";
|
||||||
import { getLivekitRooms, kickLivekitParticipant } from "_querys/livekit";
|
import { getLivekitRooms, kickLivekitParticipant } from "_querys/livekit";
|
||||||
import { editUserAPI } from "_querys/user";
|
import { editUserAPI } from "_querys/user";
|
||||||
import { ParticipantInfo } from "livekit-server-sdk";
|
import { ParticipantInfo } from "livekit-server-sdk";
|
||||||
|
|||||||
@@ -2,17 +2,6 @@ import { ConnectedAircraft, ConnectedDispatcher, Prisma } from "@repo/db";
|
|||||||
import { serverApi } from "_helpers/axios";
|
import { serverApi } from "_helpers/axios";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
export const getConnectedUserAPI = async () => {
|
|
||||||
const res = await axios.get<(ConnectedAircraft | ConnectedDispatcher)[]>(
|
|
||||||
"/api/connected-user",
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
if (res.status !== 200) {
|
|
||||||
throw new Error("Failed to fetch Connected User");
|
|
||||||
}
|
|
||||||
return res.data;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const changeDispatcherAPI = async (
|
export const changeDispatcherAPI = async (
|
||||||
id: number,
|
id: number,
|
||||||
data: Prisma.ConnectedDispatcherUpdateInput,
|
data: Prisma.ConnectedDispatcherUpdateInput,
|
||||||
@@ -9,6 +9,8 @@ import { ConnectionQuality, Participant, Room, RoomEvent, RpcInvocationData } fr
|
|||||||
import { pilotSocket } from "pilot/socket";
|
import { pilotSocket } from "pilot/socket";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { useDispatchConnectionStore } from "_store/dispatch/connectionStore";
|
||||||
|
import { changeDispatcherAPI } from "_querys/dispatcher";
|
||||||
|
|
||||||
let interval: NodeJS.Timeout;
|
let interval: NodeJS.Timeout;
|
||||||
|
|
||||||
@@ -95,7 +97,7 @@ export const useAudioStore = create<TalkState>((set, get) => ({
|
|||||||
},
|
},
|
||||||
connect: async (roomName, role) => {
|
connect: async (roomName, role) => {
|
||||||
set({ state: "connecting" });
|
set({ state: "connecting" });
|
||||||
console.log("Connecting to room: ", roomName);
|
|
||||||
try {
|
try {
|
||||||
// Clean old room
|
// Clean old room
|
||||||
const connectedRoom = get().room;
|
const connectedRoom = get().room;
|
||||||
@@ -114,7 +116,15 @@ export const useAudioStore = create<TalkState>((set, get) => ({
|
|||||||
await room.prepareConnection(url, token);
|
await room.prepareConnection(url, token);
|
||||||
room
|
room
|
||||||
// Connection events
|
// Connection events
|
||||||
.on(RoomEvent.Connected, () => {
|
.on(RoomEvent.Connected, async () => {
|
||||||
|
const dispatchState = useDispatchConnectionStore.getState();
|
||||||
|
|
||||||
|
if (dispatchState.status === "connected" && dispatchState.connectedDispatcher?.id) {
|
||||||
|
changeDispatcherAPI(dispatchState.connectedDispatcher?.id, {
|
||||||
|
zone: roomName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
set({ state: "connected", room, message: null });
|
set({ state: "connected", room, message: null });
|
||||||
})
|
})
|
||||||
.on(RoomEvent.Disconnected, () => {
|
.on(RoomEvent.Disconnected, () => {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useEffect, useRef, useState } from "react";
|
|||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { useMutation } from "@tanstack/react-query";
|
import { useMutation } from "@tanstack/react-query";
|
||||||
import { Prisma } from "@repo/db";
|
import { Prisma } from "@repo/db";
|
||||||
import { changeDispatcherAPI } from "_querys/connected-user";
|
import { changeDispatcherAPI } from "_querys/dispatcher";
|
||||||
|
|
||||||
export const ConnectionBtn = () => {
|
export const ConnectionBtn = () => {
|
||||||
const modalRef = useRef<HTMLDialogElement>(null);
|
const modalRef = useRef<HTMLDialogElement>(null);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { BADGES, PublicUser } from "@repo/db";
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { Badge } from "_components/Badge/Badge";
|
import { Badge } from "_components/Badge/Badge";
|
||||||
import { getConnectedAircraftsAPI } from "_querys/aircrafts";
|
import { getConnectedAircraftsAPI } from "_querys/aircrafts";
|
||||||
import { getConnectedDispatcherAPI } from "_querys/connected-user";
|
import { getConnectedDispatcherAPI } from "_querys/dispatcher";
|
||||||
import { Plane, Workflow } from "lucide-react";
|
import { Plane, Workflow } from "lucide-react";
|
||||||
|
|
||||||
export const ConnectedDispatcher = () => {
|
export const ConnectedDispatcher = () => {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { BADGES, PublicUser } from "@repo/db";
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { Badge } from "_components/Badge/Badge";
|
import { Badge } from "_components/Badge/Badge";
|
||||||
import { getConnectedAircraftsAPI } from "_querys/aircrafts";
|
import { getConnectedAircraftsAPI } from "_querys/aircrafts";
|
||||||
import { getConnectedDispatcherAPI } from "_querys/connected-user";
|
import { getConnectedDispatcherAPI } from "_querys/dispatcher";
|
||||||
import { Plane, Workflow } from "lucide-react";
|
import { Plane, Workflow } from "lucide-react";
|
||||||
|
|
||||||
export const ConnectedDispatcher = () => {
|
export const ConnectedDispatcher = () => {
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
"i": "^0.3.7",
|
"i": "^0.3.7",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
|
"leaflet.polylinemeasure": "^3.0.0",
|
||||||
"livekit-client": "^2.13.3",
|
"livekit-client": "^2.13.3",
|
||||||
"livekit-server-sdk": "^2.13.0",
|
"livekit-server-sdk": "^2.13.0",
|
||||||
"lucide-react": "^0.511.0",
|
"lucide-react": "^0.511.0",
|
||||||
|
|||||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@@ -150,6 +150,9 @@ importers:
|
|||||||
leaflet:
|
leaflet:
|
||||||
specifier: ^1.9.4
|
specifier: ^1.9.4
|
||||||
version: 1.9.4
|
version: 1.9.4
|
||||||
|
leaflet.polylinemeasure:
|
||||||
|
specifier: ^3.0.0
|
||||||
|
version: 3.0.0
|
||||||
livekit-client:
|
livekit-client:
|
||||||
specifier: ^2.13.3
|
specifier: ^2.13.3
|
||||||
version: 2.13.3(@types/dom-mediacapture-record@1.0.22)
|
version: 2.13.3(@types/dom-mediacapture-record@1.0.22)
|
||||||
@@ -3573,6 +3576,9 @@ packages:
|
|||||||
leac@0.6.0:
|
leac@0.6.0:
|
||||||
resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==}
|
resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==}
|
||||||
|
|
||||||
|
leaflet.polylinemeasure@3.0.0:
|
||||||
|
resolution: {integrity: sha512-PTTHz7NBJiWmNFetH8hb3leQNM15qoM9Xe7VDKh1Hyat/M+USqFYeoa0A0LAecsqsjUqA63Rzux4SivoyrSFkA==}
|
||||||
|
|
||||||
leaflet@1.9.4:
|
leaflet@1.9.4:
|
||||||
resolution: {integrity: sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==}
|
resolution: {integrity: sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==}
|
||||||
|
|
||||||
@@ -9279,6 +9285,8 @@ snapshots:
|
|||||||
|
|
||||||
leac@0.6.0: {}
|
leac@0.6.0: {}
|
||||||
|
|
||||||
|
leaflet.polylinemeasure@3.0.0: {}
|
||||||
|
|
||||||
leaflet@1.9.4: {}
|
leaflet@1.9.4: {}
|
||||||
|
|
||||||
levn@0.4.1:
|
levn@0.4.1:
|
||||||
|
|||||||
Reference in New Issue
Block a user