From 08c4cfe082c6345c46257c2748ff49b71ad13a5e Mon Sep 17 00:00:00 2001 From: PxlLoewe <72106766+PxlLoewe@users.noreply.github.com> Date: Thu, 24 Jul 2025 14:53:40 -0700 Subject: [PATCH] dev, Dispatch + Pilot Settings --- .../dispatch/_components/navbar/Navbar.tsx | 10 +- .../navbar/_components/Settings.tsx | 241 ++++++++++++++++++ .../(app)/pilot/_components/navbar/Navbar.tsx | 10 +- .../navbar/_components}/Settings.tsx | 0 .../map/_components/MarkerCluster.tsx | 2 +- apps/hub/app/(app)/_components/Changelog.tsx | 11 +- .../(app)/_components/ChangelogActions.tsx | 6 +- .../migration.sql | 2 + packages/database/prisma/schema/user.prisma | 17 +- 9 files changed, 268 insertions(+), 31 deletions(-) create mode 100644 apps/dispatch/app/(app)/dispatch/_components/navbar/_components/Settings.tsx rename apps/dispatch/app/{_components/navbar => (app)/pilot/_components/navbar/_components}/Settings.tsx (100%) create mode 100644 packages/database/prisma/schema/migrations/20250724214505_dispatcher_auto_close/migration.sql diff --git a/apps/dispatch/app/(app)/dispatch/_components/navbar/Navbar.tsx b/apps/dispatch/app/(app)/dispatch/_components/navbar/Navbar.tsx index 4be4b3c4..8bf13e2e 100644 --- a/apps/dispatch/app/(app)/dispatch/_components/navbar/Navbar.tsx +++ b/apps/dispatch/app/(app)/dispatch/_components/navbar/Navbar.tsx @@ -2,7 +2,7 @@ import { Connection } from "./_components/Connection"; import { Audio } from "../../../../_components/Audio/Audio"; import { ExitIcon, ExternalLinkIcon } from "@radix-ui/react-icons"; import Link from "next/link"; -import { Settings } from "_components/navbar/Settings"; +import { Settings } from "./_components/Settings"; import AdminPanel from "_components/navbar/AdminPanel"; import { getServerSession } from "api/auth/[...nextauth]/auth"; import { WarningAlert } from "_components/navbar/PageAlert"; @@ -12,9 +12,9 @@ export default async function Navbar() { const session = await getServerSession(); return ( -
+
-

VAR Leitstelle V2

+

VAR Leitstelle V2

{session?.user.permissions.includes("ADMIN_KICK") && }
@@ -38,12 +38,12 @@ export default async function Navbar() { rel="noopener noreferrer" >
diff --git a/apps/dispatch/app/(app)/dispatch/_components/navbar/_components/Settings.tsx b/apps/dispatch/app/(app)/dispatch/_components/navbar/_components/Settings.tsx new file mode 100644 index 00000000..1ddee742 --- /dev/null +++ b/apps/dispatch/app/(app)/dispatch/_components/navbar/_components/Settings.tsx @@ -0,0 +1,241 @@ +"use client"; +import { useEffect, useRef, useState } from "react"; +import { GearIcon } from "@radix-ui/react-icons"; +import { SettingsIcon, Volume2 } from "lucide-react"; +import MicVolumeBar from "_components/MicVolumeIndication"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { editUserAPI, getUserAPI } from "_querys/user"; +import { useSession } from "next-auth/react"; +import { useAudioStore } from "_store/audioStore"; +import toast from "react-hot-toast"; + +export const SettingsBtn = () => { + const session = useSession(); + + const [inputDevices, setInputDevices] = useState([]); + const { data: user } = useQuery({ + queryKey: ["user", session.data?.user.id], + queryFn: () => getUserAPI(session.data!.user.id), + }); + const testSoundRef = useRef(null); + + const editUserMutation = useMutation({ + mutationFn: editUserAPI, + }); + + useEffect(() => { + if (typeof window !== "undefined") { + testSoundRef.current = new Audio("/sounds/DME-new-mission.wav"); + } + }, []); + + const modalRef = useRef(null); + + const [showIndication, setShowIndication] = useState(false); + + const [settings, setSettings] = useState({ + micDeviceId: user?.settingsMicDevice || null, + micVolume: user?.settingsMicVolume || 1, + radioVolume: user?.settingsRadioVolume || 0.8, + autoCloseMapPopup: user?.settingsAutoCloseMapPopup || false, + }); + + const { setSettings: setAudioSettings } = useAudioStore((state) => state); + + useEffect(() => { + if (user) { + setAudioSettings({ + micDeviceId: user.settingsMicDevice, + micVolume: user.settingsMicVolume || 1, + radioVolume: user.settingsRadioVolume || 0.8, + dmeVolume: user.settingsDmeVolume || 0.8, + }); + setSettings({ + micDeviceId: user.settingsMicDevice, + micVolume: user.settingsMicVolume || 1, + radioVolume: user.settingsRadioVolume || 0.8, + autoCloseMapPopup: user.settingsAutoCloseMapPopup || false, + }); + } + }, [user, setSettings, setAudioSettings]); + + const setSettingsPartial = (newSettings: Partial) => { + setSettings((prev) => ({ + ...prev, + ...newSettings, + })); + }; + + useEffect(() => { + const setDevices = async () => { + if (typeof navigator !== "undefined" && navigator.mediaDevices?.enumerateDevices) { + const stream = await navigator.mediaDevices.getUserMedia({ video: false, audio: true }); + const devices = await navigator.mediaDevices.enumerateDevices(); + setInputDevices(devices.filter((d) => d.kind === "audioinput")); + stream.getTracks().forEach((track) => track.stop()); + } + }; + + setDevices(); + }, []); + + return ( +
+ + + +
+

+ Einstellungen +

+
+
+ +
+

+ Eingabelautstärke +

+
+ { + const value = parseFloat(e.target.value); + setSettingsPartial({ micVolume: value }); + setShowIndication(true); + }} + value={settings.micVolume} + className="range range-xs range-accent w-full" + /> +
+ 0% + 25% + 50% + 75% + 100% +
+
+ {showIndication && ( + + )} +
+
+

+ Funk Lautstärke +

+
+ { + const value = parseFloat(e.target.value); + setSettingsPartial({ radioVolume: value }); + }} + value={settings.radioVolume} + className="range range-xs range-primary w-full" + /> +
+ 0% + 25% + 50% + 75% + 100% +
+
+ +
+
+
+ Login options + +
+
+
+ +
+ + +
+
+
+
+ ); +}; +export const Settings = () => { + return ( +
+ +
+ ); +}; diff --git a/apps/dispatch/app/(app)/pilot/_components/navbar/Navbar.tsx b/apps/dispatch/app/(app)/pilot/_components/navbar/Navbar.tsx index ca38fe35..612646b9 100644 --- a/apps/dispatch/app/(app)/pilot/_components/navbar/Navbar.tsx +++ b/apps/dispatch/app/(app)/pilot/_components/navbar/Navbar.tsx @@ -2,15 +2,15 @@ import { Connection } from "./_components/Connection"; import { Audio } from "_components/Audio/Audio"; import { ExitIcon, ExternalLinkIcon } from "@radix-ui/react-icons"; import Link from "next/link"; -import { Settings } from "_components/navbar/Settings"; +import { Settings } from "./_components/Settings"; import { WarningAlert } from "_components/navbar/PageAlert"; import { Radar } from "lucide-react"; export default function Navbar() { return ( -
+
-

VAR Operations Center

+

VAR Operations Center

@@ -33,12 +33,12 @@ export default function Navbar() { rel="noopener noreferrer" >
diff --git a/apps/dispatch/app/_components/navbar/Settings.tsx b/apps/dispatch/app/(app)/pilot/_components/navbar/_components/Settings.tsx similarity index 100% rename from apps/dispatch/app/_components/navbar/Settings.tsx rename to apps/dispatch/app/(app)/pilot/_components/navbar/_components/Settings.tsx diff --git a/apps/dispatch/app/_components/map/_components/MarkerCluster.tsx b/apps/dispatch/app/_components/map/_components/MarkerCluster.tsx index 6b7ce508..e392dc39 100644 --- a/apps/dispatch/app/_components/map/_components/MarkerCluster.tsx +++ b/apps/dispatch/app/_components/map/_components/MarkerCluster.tsx @@ -216,7 +216,7 @@ export const MarkerCluster = () => { const lng = aircraft.posLng!; const existingClusterIndex = newCluster.findIndex( - (c) => Math.abs(c.lat - lat) < 1 && Math.abs(c.lng - lng) < 1, + (c) => Math.abs(c.lat - lat) < 1.55 && Math.abs(c.lng - lng) < 1, ); const existingCluster = newCluster[existingClusterIndex]; if (existingCluster) { diff --git a/apps/hub/app/(app)/_components/Changelog.tsx b/apps/hub/app/(app)/_components/Changelog.tsx index 45ed9d63..a70b0f15 100644 --- a/apps/hub/app/(app)/_components/Changelog.tsx +++ b/apps/hub/app/(app)/_components/Changelog.tsx @@ -5,13 +5,14 @@ import { Button } from "@repo/shared-components"; import MDEditor from "@uiw/react-md-editor"; import { RefreshCw } from "lucide-react"; import { updateChangelogAck } from "./ChangelogActions"; +import { Changelog } from "@repo/db"; export const ChangelogModal = ({ latestChangelog, isOpen, onClose, }: { - latestChangelog: { title: string; text: string; previewImage: string } | null; + latestChangelog: Changelog | null; isOpen: boolean; onClose: () => void; }) => { @@ -79,11 +80,7 @@ export const ChangelogModal = ({ ); }; -export const ChangelogBtn = ({ - latestChangelog, -}: { - latestChangelog: { title: string; text: string; previewImage: string } | null; -}) => { +export const ChangelogBtn = ({ latestChangelog }: { latestChangelog: Changelog | null }) => { const [isOpen, setIsOpen] = useState(false); if (!latestChangelog) return null; @@ -109,7 +106,7 @@ export const ChangelogBtn = ({ export const OpenChangelogOnPageload = ({ latestChangelog, }: { - latestChangelog: { title: string; text: string; previewImage: string } | null; + latestChangelog: Changelog | null; }) => { const [isOpen, setIsOpen] = useState(true); diff --git a/apps/hub/app/(app)/_components/ChangelogActions.tsx b/apps/hub/app/(app)/_components/ChangelogActions.tsx index c446c81b..7a9ca313 100644 --- a/apps/hub/app/(app)/_components/ChangelogActions.tsx +++ b/apps/hub/app/(app)/_components/ChangelogActions.tsx @@ -12,11 +12,7 @@ export async function getLatestChangelog() { }); if (latestChangelog.length > 0 && latestChangelog[0]) { - return { - title: latestChangelog[0].title, - text: latestChangelog[0].text, - previewImage: latestChangelog[0].previewImage || "", - }; + return latestChangelog[0]; } return null; diff --git a/packages/database/prisma/schema/migrations/20250724214505_dispatcher_auto_close/migration.sql b/packages/database/prisma/schema/migrations/20250724214505_dispatcher_auto_close/migration.sql new file mode 100644 index 00000000..38e5ea56 --- /dev/null +++ b/packages/database/prisma/schema/migrations/20250724214505_dispatcher_auto_close/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "users" ADD COLUMN "settings_auto_close_map_popup" BOOLEAN NOT NULL DEFAULT false; diff --git a/packages/database/prisma/schema/user.prisma b/packages/database/prisma/schema/user.prisma index 5769cf1b..f7a84c2e 100644 --- a/packages/database/prisma/schema/user.prisma +++ b/packages/database/prisma/schema/user.prisma @@ -37,14 +37,15 @@ model User { changelogAck Boolean @default(false) // Settings: - pathSelected Boolean @default(false) - migratedFromV1 Boolean @default(false) - settingsNtfyRoom String? @map(name: "settings_ntfy_room") - settingsMicDevice String? @map(name: "settings_mic_device") - settingsMicVolume Float? @map(name: "settings_mic_volume") - settingsDmeVolume Float? @map(name: "settings_dme_volume") - settingsRadioVolume Float? @map(name: "settings_funk_volume") - settingsHideLastname Boolean @default(false) @map(name: "settings_hide_lastname") + pathSelected Boolean @default(false) + migratedFromV1 Boolean @default(false) + settingsNtfyRoom String? @map(name: "settings_ntfy_room") + settingsMicDevice String? @map(name: "settings_mic_device") + settingsMicVolume Float? @map(name: "settings_mic_volume") + settingsDmeVolume Float? @map(name: "settings_dme_volume") + settingsRadioVolume Float? @map(name: "settings_funk_volume") + settingsHideLastname Boolean @default(false) @map(name: "settings_hide_lastname") + settingsAutoCloseMapPopup Boolean @default(false) @map(name: "settings_auto_close_map_popup") // email Verification: emailVerificationToken String? @map(name: "email_verification_token")