diff --git a/apps/dispatch-server/modules/mission.ts b/apps/dispatch-server/modules/mission.ts index e79c95a8..4f9fe07c 100644 --- a/apps/dispatch-server/modules/mission.ts +++ b/apps/dispatch-server/modules/mission.ts @@ -14,113 +14,118 @@ export const sendAlert = async ( connectedAircrafts: ConnectedAircraft[]; mission: Mission; }> => { - const mission = await prisma.mission.findUnique({ - where: { id: id }, - }); - const Stations = await prisma.station.findMany({ - where: { - id: { - in: mission?.missionStationIds, - }, - }, - }); - - if (!mission) { - throw new Error("Mission not found"); - } - - // connectedAircrafts the alert is sent to - const connectedAircrafts = await prisma.connectedAircraft.findMany({ - where: { - stationId: stationId - ? stationId - : { - in: mission.missionStationIds, - }, - logoutTime: null, - }, - include: { - Station: true, - }, - }); - - for (const aircraft of connectedAircrafts) { - io.to(`station:${aircraft.stationId}`).emit("mission-alert", { - ...mission, - Stations, + try { + const mission = await prisma.mission.findUnique({ + where: { id: id }, }); - io.to(`desktop:${aircraft.userId}`).emit("mission-alert", { - missionId: mission.id, - }); - - const user = await prisma.user.findUnique({ - where: { id: aircraft.userId }, - }); - if (!user) continue; - if (user.settingsNtfyRoom) { - await sendNtfyMission(mission, Stations, aircraft.Station, user.settingsNtfyRoom); - } - const existingMissionOnStationUser = await prisma.missionOnStationUsers.findFirst({ + const Stations = await prisma.station.findMany({ where: { - missionId: mission.id, - userId: aircraft.userId, - stationId: aircraft.stationId, + id: { + in: mission?.missionStationIds, + }, }, }); - if (!existingMissionOnStationUser) - await prisma.missionOnStationUsers.create({ - data: { + if (!mission) { + throw new Error("Mission not found"); + } + + // connectedAircrafts the alert is sent to + const connectedAircrafts = await prisma.connectedAircraft.findMany({ + where: { + stationId: stationId + ? stationId + : { + in: mission.missionStationIds, + }, + logoutTime: null, + }, + include: { + Station: true, + }, + }); + + for (const aircraft of connectedAircrafts) { + io.to(`station:${aircraft.stationId}`).emit("mission-alert", { + ...mission, + Stations, + }); + io.to(`desktop:${aircraft.userId}`).emit("mission-alert", { + missionId: mission.id, + }); + + const user = await prisma.user.findUnique({ + where: { id: aircraft.userId }, + }); + if (!user) continue; + if (user.settingsNtfyRoom) { + await sendNtfyMission(mission, Stations, aircraft.Station, user.settingsNtfyRoom); + } + const existingMissionOnStationUser = await prisma.missionOnStationUsers.findFirst({ + where: { missionId: mission.id, userId: aircraft.userId, stationId: aircraft.stationId, }, }); - } - // for statistics only - await prisma.missionsOnStations - .createMany({ - data: mission.missionStationIds.map((stationId) => ({ - missionId: mission.id, - stationId, - })), - }) - .catch((err) => { - // Ignore if the entry already exists - }); - if (user === "HPG") { - await prisma.mission.update({ - where: { id: Number(id) }, - data: { - state: "running", - missionLog: { - push: { - type: "alert-log", - auto: true, - timeStamp: new Date().toISOString(), - } as any, + if (!existingMissionOnStationUser) + await prisma.missionOnStationUsers.create({ + data: { + missionId: mission.id, + userId: aircraft.userId, + stationId: aircraft.stationId, + }, + }); + } + + // for statistics only + await prisma.missionsOnStations + .createMany({ + data: mission.missionStationIds.map((stationId) => ({ + missionId: mission.id, + stationId, + })), + }) + .catch((err) => { + // Ignore if the entry already exists + }); + if (user === "HPG") { + await prisma.mission.update({ + where: { id: Number(id) }, + data: { + state: "running", + missionLog: { + push: { + type: "alert-log", + auto: true, + timeStamp: new Date().toISOString(), + } as any, + }, }, - }, - }); - } else { - await prisma.mission.update({ - where: { id: Number(id) }, - data: { - state: "running", - missionLog: { - push: { - type: "alert-log", - auto: false, - timeStamp: new Date().toISOString(), - data: { - stationId: stationId, - user: getPublicUser(user, { ignorePrivacy: true }), - }, - } as any, + }); + } else { + await prisma.mission.update({ + where: { id: Number(id) }, + data: { + state: "running", + missionLog: { + push: { + type: "alert-log", + auto: false, + timeStamp: new Date().toISOString(), + data: { + stationId: stationId, + user: getPublicUser(user, { ignorePrivacy: true }), + }, + } as any, + }, }, - }, - }); + }); + } + return { connectedAircrafts, mission }; + } catch (error) { + console.error("Error sending mission alert:", error); + throw new Error("Ein Fehler ist aufgetreten. Bitte melde den Fehler als Bug"); } - return { connectedAircrafts, mission }; }; diff --git a/apps/dispatch-server/modules/ntfy.ts b/apps/dispatch-server/modules/ntfy.ts index bc86df56..3ba75dde 100644 --- a/apps/dispatch-server/modules/ntfy.ts +++ b/apps/dispatch-server/modules/ntfy.ts @@ -50,10 +50,7 @@ const getRthCallsigns = (mission: Mission, stations: Station[]) => { return `🚁 RTH${callsigns.length > 1 ? "s" : ""}: ${callsigns.join(" / ")} `; }; -const getNtfyHeader = ( - mission: Mission, - clientStation: Station, -): NtfyHeader => ({ +const getNtfyHeader = (mission: Mission, clientStation: Station): NtfyHeader => ({ headers: { Title: `${clientStation.bosCallsignShort} / ${mission.missionKeywordAbbreviation} / ${mission.missionKeywordCategory}`, Tags: "pager", @@ -76,9 +73,13 @@ export const sendNtfyMission = async ( clientStation: Station, ntfyRoom: string, ) => { - axios.post( - `https://ntfy.sh/${ntfyRoom}`, - getNtfyData(mission, stations), - getNtfyHeader(mission, clientStation), - ); + try { + await axios.post( + `https://ntfy.sh/${ntfyRoom}`, + getNtfyData(mission, stations), + getNtfyHeader(mission, clientStation), + ); + } catch (error) { + console.error("Error sending Ntfy mission:", error); + } }; diff --git a/apps/dispatch-server/routes/dispatcher.ts b/apps/dispatch-server/routes/dispatcher.ts index 7adc9c6d..03cc314b 100644 --- a/apps/dispatch-server/routes/dispatcher.ts +++ b/apps/dispatch-server/routes/dispatcher.ts @@ -34,7 +34,7 @@ router.patch("/:id", async (req, res) => { }, }); - if (discordAccount?.id) { + if (discordAccount?.id && !disaptcherUpdate.ghostMode) { await renameMember( discordAccount.discordId.toString(), `${getPublicUser(newDispatcher.user).fullName} • ${newDispatcher.zone}`, diff --git a/apps/dispatch-server/routes/mission.ts b/apps/dispatch-server/routes/mission.ts index bcb18a2a..e487e59f 100644 --- a/apps/dispatch-server/routes/mission.ts +++ b/apps/dispatch-server/routes/mission.ts @@ -189,7 +189,11 @@ router.post("/:id/send-alert", async (req, res) => { return; } catch (error) { console.error(error); - res.status(500).json({ error: "Failed to send mission" }); + res + .status(500) + .json({ + error: `Ein Fehler ist aufgetreten. Bitte melde den Fehler als Bug (${(error as Error).message})`, + }); return; } }); diff --git a/apps/dispatch-server/socket-events/connect-dispatch.ts b/apps/dispatch-server/socket-events/connect-dispatch.ts index 853e1d7c..f2acb6c9 100644 --- a/apps/dispatch-server/socket-events/connect-dispatch.ts +++ b/apps/dispatch-server/socket-events/connect-dispatch.ts @@ -99,7 +99,7 @@ export const handleConnectDispatch = io.to("dispatchers").emit("dispatchers-update"); io.to("pilots").emit("dispatchers-update"); - if (discordAccount?.id && !ghostMode) { + if (discordAccount?.id) { await renameMember( discordAccount.discordId.toString(), `${getPublicUser(user).fullName} - ${user.publicId}`, diff --git a/apps/dispatch/Dockerfile b/apps/dispatch/Dockerfile index 0923d88c..315d8d76 100644 --- a/apps/dispatch/Dockerfile +++ b/apps/dispatch/Dockerfile @@ -6,12 +6,14 @@ ARG NEXT_PUBLIC_HUB_URL ARG NEXT_PUBLIC_DISPATCH_SERVICE_ID ARG NEXT_PUBLIC_LIVEKIT_URL ARG NEXT_PUBLIC_DISCORD_URL +ARG NEXT_PUBLIC_OPENAIP_ACCESS ENV NEXT_PUBLIC_DISPATCH_SERVER_URL=$NEXT_PUBLIC_DISPATCH_SERVER_URL ENV NEXT_PUBLIC_DISPATCH_URL=$NEXT_PUBLIC_DISPATCH_URL ENV NEXT_PUBLIC_HUB_URL=$NEXT_PUBLIC_HUB_URL ENV NEXT_PUBLIC_DISPATCH_SERVICE_ID=$NEXT_PUBLIC_DISPATCH_SERVICE_ID ENV NEXT_PUBLIC_LIVEKIT_URL=$NEXT_PUBLIC_LIVEKIT_URL +ENV NEXT_PUBLIC_OPENAIP_ACCESS=$NEXT_PUBLIC_OPENAIP_ACCESS ENV NEXT_PUBLIC_DISCORD_URL=$NEXT_PUBLIC_DISCORD_URL ENV PNPM_HOME="/usr/local/pnpm" diff --git a/apps/dispatch/app/_components/Audio/Audio.tsx b/apps/dispatch/app/_components/Audio/Audio.tsx index abe069a6..8b908570 100644 --- a/apps/dispatch/app/_components/Audio/Audio.tsx +++ b/apps/dispatch/app/_components/Audio/Audio.tsx @@ -25,6 +25,7 @@ import { useSounds } from "_components/Audio/useSounds"; export const Audio = () => { const { speakingParticipants, + resetSpeakingParticipants, isTalking, toggleTalking, transmitBlocked, @@ -104,7 +105,7 @@ export const Audio = () => { data-tip="Nachricht entfernen" > {state === "connected" && (
- {connectionQuality === ConnectionQuality.Excellent && } - {connectionQuality === ConnectionQuality.Good && } - {connectionQuality === ConnectionQuality.Poor && } - {connectionQuality === ConnectionQuality.Lost && } + {connectionQuality === ConnectionQuality.Excellent && } + {connectionQuality === ConnectionQuality.Good && } + {connectionQuality === ConnectionQuality.Poor && } + {connectionQuality === ConnectionQuality.Lost && } {connectionQuality === ConnectionQuality.Unknown && ( - + )}
{remoteParticipants}
@@ -184,7 +186,7 @@ export const Audio = () => { {ROOMS.map((r) => (
  • @@ -201,12 +203,12 @@ export const Audio = () => { ))}
  • diff --git a/apps/dispatch/app/_components/left/Chat.tsx b/apps/dispatch/app/_components/left/Chat.tsx index d09a5ebf..93b17997 100644 --- a/apps/dispatch/app/_components/left/Chat.tsx +++ b/apps/dispatch/app/_components/left/Chat.tsx @@ -10,9 +10,11 @@ import { getConnectedDispatcherAPI } from "_querys/dispatcher"; import { getConnectedAircraftsAPI } from "_querys/aircrafts"; import { useDispatchConnectionStore } from "_store/dispatch/connectionStore"; import { usePilotConnectionStore } from "_store/pilot/connectionStore"; +import { Trash } from "lucide-react"; export const Chat = () => { const { + removeChat, setReportTabOpen, chatOpen, setChatOpen, @@ -50,13 +52,21 @@ export const Chat = () => { setOwnId(session.data?.user.id); }, [session.data?.user.id, setOwnId]); - const filteredDispatcher = dispatcher?.filter((d) => d.userId !== session.data?.user.id); + const filteredDispatcher = dispatcher?.filter( + (d) => d.userId !== session.data?.user.id && !chats[d.userId], + ); const filteredAircrafts = aircrafts?.filter( - (a) => a.userId !== session.data?.user.id && dispatcherConnected, + (a) => a.userId !== session.data?.user.id && dispatcherConnected && chats[a.userId], ); const btnActive = pilotConnected || dispatcherConnected; + useEffect(() => { + if (!filteredDispatcher?.length && !filteredAircrafts?.length) { + setAddTabValue("default"); + } + }, [filteredDispatcher, filteredAircrafts]); + useEffect(() => { if (!btnActive) { setChatOpen(false); @@ -146,13 +156,17 @@ export const Chat = () => {
    + {!changelog?.id && ( + + )}