added missing Settings functionality, moved ntfy setting

This commit is contained in:
PxlLoewe
2025-07-17 01:08:10 -07:00
parent 44427a1b4b
commit 11b1d8745d
9 changed files with 141 additions and 150 deletions

View File

@@ -47,27 +47,28 @@ export default function MicrophoneLevel({ deviceId, volumeInput }: MicrophoneLev
};
}, [deviceId, volumeInput]);
const barWidth = Math.max((volumeLevel / 70) * 100 - 35, 0);
const barWidth = Math.min((volumeLevel / 140) * 100, 100);
return (
<div className="w-full">
<div className="relative w-full bg-base-300 h-5 rounded">
<div
className={cn("bg-primary h-full rounded", barWidth > 100 && "bg-red-400")}
className={cn("bg-primary h-full rounded", barWidth == 100 && "bg-red-400")}
style={{
width: `${barWidth > 100 ? 100 : barWidth}%`,
width: `${barWidth}%`,
transition: "width 0.2s",
}}
/>
<div
className="absolute top-0 left-[60%] w-[20%] h-full bg-green-500 opacity-40 rounded"
className="absolute top-0 left-[60%] w-[30%] h-full bg-green-500 opacity-40 rounded"
style={{
transform: "translateX(-50%)",
}}
/>
</div>
<p className="text-gray-500 text-sm">
Lautstärke sollte beim Sprechen in dem Grünen bereich bleiben
Lautstärke sollte beim Sprechen in dem Grünen bereich bleiben. Beachte das scharfe Laute
(z.B. "S" oder "Z") die Anzeige verfälschen können.
</p>
</div>
);

View File

@@ -73,7 +73,6 @@ const HeliportsLayer = () => {
queryKey: ["heliports"],
queryFn: () => getHeliportsAPI(),
});
console.log("Heliports Layer", heliports);
const [heliportsWithIcon, setHeliportsWithIcon] = useState<(Heliport & { icon?: string })[]>([]);
const map = useMap();
const [isVisible, setIsVisible] = useState(true);

View File

@@ -1,17 +1,19 @@
"use client";
import { useEffect, useRef, useState } from "react";
import { GearIcon } from "@radix-ui/react-icons";
import { SettingsIcon, Volume2 } from "lucide-react";
import { Bell, 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";
import Link from "next/link";
export const SettingsBtn = () => {
const session = useSession();
const [inputDevices, setInputDevices] = useState<MediaDeviceInfo[]>([]);
const { data: user } = useQuery({
queryKey: ["user", session.data?.user.id],
queryFn: () => getUserAPI(session.data!.user.id),
@@ -30,26 +32,42 @@ export const SettingsBtn = () => {
const modalRef = useRef<HTMLDialogElement>(null);
const [inputDevices, setInputDevices] = useState<MediaDeviceInfo[]>([]);
const [selectedDevice, setSelectedDevice] = useState<string | null>(
user?.settingsMicDevice || null,
);
const [showIndication, setShowIndication] = useState<boolean>(false);
const [micVol, setMicVol] = useState<number>(1);
const [funkVolume, setFunkVol] = useState<number>(0.8);
const [dmeVolume, setDmeVol] = useState<number>(0.8);
const { setMic } = useAudioStore((state) => state);
const [settings, setSettings] = useState({
micDeviceId: user?.settingsMicDevice || null,
micVolume: user?.settingsMicVolume || 1,
radioVolume: user?.settingsRadioVolume || 0.8,
dmeVolume: user?.settingsDmeVolume || 0.8,
pilotNtfyRoom: user?.settingsNtfyRoom || "",
});
const { setSettings: setAudioSettings } = useAudioStore((state) => state);
useEffect(() => {
if (user) {
setSelectedDevice(user.settingsMicDevice);
setMic(user.settingsMicDevice, user.settingsMicVolume || 1);
setMicVol(user.settingsMicVolume || 1);
setFunkVol(user.settingsRadioVolume || 0.8);
setDmeVol(user.settingsDmeVolume || 0.8);
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,
dmeVolume: user.settingsDmeVolume || 0.8,
pilotNtfyRoom: user.settingsNtfyRoom || "",
});
}
}, [user, setMic]);
}, [user, setSettings, setAudioSettings]);
const setSettingsPartial = (newSettings: Partial<typeof settings>) => {
setSettings((prev) => ({
...prev,
...newSettings,
}));
};
useEffect(() => {
const setDevices = async () => {
@@ -87,9 +105,9 @@ export const SettingsBtn = () => {
<span>Eingabegerät</span>
<select
className="input w-full"
value={selectedDevice ? selectedDevice : ""}
value={settings.micDeviceId ? settings.micDeviceId : ""}
onChange={(e) => {
setSelectedDevice(e.target.value);
setSettingsPartial({ micDeviceId: e.target.value });
setShowIndication(true);
}}
>
@@ -115,10 +133,10 @@ export const SettingsBtn = () => {
step={0.01}
onChange={(e) => {
const value = parseFloat(e.target.value);
setMicVol(value);
setSettingsPartial({ micVolume: value });
setShowIndication(true);
}}
value={micVol}
value={settings.micVolume}
className="range range-xs range-accent w-full"
/>
<div className="flex justify-between px-2.5 mt-2 text-xs">
@@ -130,7 +148,10 @@ export const SettingsBtn = () => {
</div>
</div>
{showIndication && (
<MicVolumeBar deviceId={selectedDevice ? selectedDevice : ""} volumeInput={micVol} />
<MicVolumeBar
deviceId={settings.micDeviceId ? settings.micDeviceId : ""}
volumeInput={settings.micVolume}
/>
)}
<div className="divider w-full" />
</div>
@@ -144,10 +165,11 @@ export const SettingsBtn = () => {
max={1}
step={0.01}
onChange={(e) => {
console.log("Radio Volume", e.target.value);
const value = parseFloat(e.target.value);
setFunkVol(value);
setSettingsPartial({ radioVolume: value });
}}
value={funkVolume}
value={settings.radioVolume}
className="range range-xs range-primary w-full"
/>
<div className="flex justify-between px-2.5 mt-2 text-xs">
@@ -158,9 +180,7 @@ export const SettingsBtn = () => {
<span>100%</span>
</div>
</div>
<div className="flex justify-center w-full">
<div className="divider w-1/2" />
</div>
<p className="flex items-center gap-2 text-base mb-2">
<Volume2 size={20} /> Melder Lautstärke
</p>
@@ -172,12 +192,12 @@ export const SettingsBtn = () => {
step={0.01}
onChange={(e) => {
const value = parseFloat(e.target.value);
setDmeVol(value);
setSettingsPartial({ dmeVolume: value });
if (!testSoundRef.current) return;
testSoundRef.current.volume = value;
testSoundRef.current.play();
}}
value={dmeVolume}
value={settings.dmeVolume}
className="range range-xs range-primary w-full"
/>
<div className="flex justify-between px-2.5 mt-2 text-xs">
@@ -188,6 +208,34 @@ export const SettingsBtn = () => {
<span>100%</span>
</div>
</div>
<div className="flex justify-center w-full">
<div className="divider w-full" />
</div>
<div className="w-full">
<label className="floating-label w-full">
<span className="text-lg flex items-center gap-2">
<Bell /> NTFY room
</span>
<input
placeholder="Erhalte eine Benachrichtigung auf dein Handy über NTFY"
className="input input-bordered w-full"
value={settings.pilotNtfyRoom}
onChange={(e) => setSettingsPartial({ pilotNtfyRoom: e.target.value })}
/>
</label>
<p className="label mt-2 w-full">
<Link
href="https://docs.virtualairrescue.com/docs/Leitstelle/App-Alarmierung#download"
target="_blank"
rel="noopener noreferrer"
className="link link-hover link-primary"
>
Hier
</Link>
findest du mehr Informationen!
</p>
</div>
<div className="flex justify-between modal-action">
<button
@@ -210,13 +258,19 @@ export const SettingsBtn = () => {
await editUserMutation.mutateAsync({
id: session.data!.user.id,
user: {
settingsMicDevice: selectedDevice,
settingsMicVolume: micVol,
settingsRadioVolume: funkVolume,
settingsDmeVolume: dmeVolume,
settingsMicDevice: settings.micDeviceId,
settingsMicVolume: settings.micVolume,
settingsRadioVolume: settings.radioVolume,
settingsDmeVolume: settings.dmeVolume,
settingsNtfyRoom: settings.pilotNtfyRoom,
},
});
setMic(selectedDevice, micVol);
setAudioSettings({
micDeviceId: settings.micDeviceId,
micVolume: settings.micVolume,
radioVolume: settings.radioVolume,
dmeVolume: settings.dmeVolume,
});
modalRef.current?.close();
toast.success("Einstellungen gespeichert");
}}
@@ -229,7 +283,6 @@ export const SettingsBtn = () => {
</div>
);
};
export const Settings = () => {
return (
<div>