(feat): add preview button to the call settings page
Some checks failed
/ build-desktop (linux) (push) Failing after 6m25s
/ build-web (push) Successful in 7m29s
/ build-mobile (push) Successful in 19m23s
/ release (push) Has been skipped

This commit is contained in:
Alois 2026-07-30 23:57:24 +02:00
commit 5a26c2d401
Signed by: alois
SSH key fingerprint: SHA256:GBzT2DXvAuGV9XIV5W3WrzVpjU54FThmxHXdbz95J24

View file

@ -1,4 +1,5 @@
import { import {
Button,
Label, Label,
Select, Select,
SelectContent, SelectContent,
@ -7,8 +8,10 @@ import {
SelectValue, SelectValue,
} from "@methanium/ui"; } from "@methanium/ui";
import { settingsStorageDefaults } from "@tensamin/shared/settings"; import { settingsStorageDefaults } from "@tensamin/shared/settings";
import { playSound, stopSound } from "@tensamin/shared/sounds";
import { useStorage } from "@tensamin/storage/context"; import { useStorage } from "@tensamin/storage/context";
import { useEffect, useState } from "react"; import { Play, Square } from "lucide-react";
import { useEffect, useRef, useState } from "react";
type CallJingle = (typeof settingsStorageDefaults)["settings.call_jingle"]; type CallJingle = (typeof settingsStorageDefaults)["settings.call_jingle"];
@ -19,33 +22,72 @@ const jingleLabels: Record<CallJingle, string> = {
export default function Page() { export default function Page() {
const { load, save } = useStorage(); const { load, save } = useStorage();
const preview = useRef<HTMLAudioElement>(null);
const [jingle, setJingle] = useState<CallJingle>( const [jingle, setJingle] = useState<CallJingle>(
settingsStorageDefaults["settings.call_jingle"], settingsStorageDefaults["settings.call_jingle"],
); );
const [isPreviewing, setIsPreviewing] = useState(false);
function stopPreview() {
stopSound(preview.current);
preview.current = null;
setIsPreviewing(false);
}
useEffect(() => { useEffect(() => {
void load("settings.call_jingle").then(setJingle); void load("settings.call_jingle").then(setJingle);
}, [load]); }, [load]);
useEffect(() => () => stopSound(preview.current), []);
return ( return (
<div className="flex max-w-md flex-col gap-2"> <div className="flex max-w-md flex-col gap-2">
<Label htmlFor="call-jingle">Outgoing call jingle</Label> <Label htmlFor="call-jingle">Outgoing call jingle</Label>
<Select <div className="flex gap-2">
value={jingle} <Select
onValueChange={(value) => { value={jingle}
const nextJingle = value ?? "jingle_1"; onValueChange={(value) => {
setJingle(nextJingle); const nextJingle = value ?? "jingle_1";
void save("settings.call_jingle", nextJingle); stopPreview();
}} setJingle(nextJingle);
> void save("settings.call_jingle", nextJingle);
<SelectTrigger className="w-full" id="call-jingle"> }}
<SelectValue>{jingleLabels[jingle]}</SelectValue> >
</SelectTrigger> <SelectTrigger className="w-full" id="call-jingle">
<SelectContent className="p-1"> <SelectValue>{jingleLabels[jingle]}</SelectValue>
<SelectItem value="jingle_1">Jingle 1</SelectItem> </SelectTrigger>
<SelectItem value="jingle_2">Jingle 2</SelectItem> <SelectContent className="p-1">
</SelectContent> <SelectItem value="jingle_1">Jingle 1</SelectItem>
</Select> <SelectItem value="jingle_2">Jingle 2</SelectItem>
</SelectContent>
</Select>
<Button
variant="outline"
aria-label={isPreviewing ? "Stop jingle preview" : "Preview jingle"}
title={isPreviewing ? "Stop preview" : "Preview jingle"}
onClick={() => {
if (isPreviewing) {
stopPreview();
return;
}
const audio = playSound(
jingle === "jingle_2" ? "call_jingle_2" : "call_jingle_1",
);
preview.current = audio;
setIsPreviewing(true);
audio.addEventListener(
"ended",
() => {
if (preview.current === audio) stopPreview();
},
{ once: true },
);
}}
>
{isPreviewing ? <Square /> : <Play />}
</Button>
</div>
</div> </div>
); );
} }