feat(frontend): always-visible Enable audio + Enable mic controls
Surface audio-playback unlock and mic publish as persistent buttons in the pod control row (alongside Test audio / Test PodMan voice / Share screen), replacing the conditional 'Sound is off' banner that only appeared after a refresh. Enable mic calls setMicrophoneEnabled, which triggers the browser permission prompt so users can verify their mic is set up right. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,8 @@ import {
|
||||
GitBranchIcon,
|
||||
ExternalLinkIcon,
|
||||
MessageSquareIcon,
|
||||
MicIcon,
|
||||
MicOffIcon,
|
||||
MonitorUpIcon,
|
||||
PanelLeftIcon,
|
||||
PanelRightIcon,
|
||||
@@ -17,6 +19,7 @@ import {
|
||||
SparklesIcon,
|
||||
TriangleAlertIcon,
|
||||
Volume2Icon,
|
||||
VolumeXIcon,
|
||||
WorkflowIcon,
|
||||
XIcon,
|
||||
} from 'lucide-react';
|
||||
@@ -113,6 +116,7 @@ export function PodView({
|
||||
const [testingVoice, setTestingVoice] = useState(false);
|
||||
const [note, setNote] = useState<string | null>(null);
|
||||
const [audioBlocked, setAudioBlocked] = useState(false);
|
||||
const [micOn, setMicOn] = useState(false);
|
||||
const [leftStreamOpen, setLeftStreamOpen] = useState(() =>
|
||||
readStoredBool('podman.myStreamOpen', true),
|
||||
);
|
||||
@@ -168,6 +172,7 @@ export function PodView({
|
||||
// are actually heard.
|
||||
const onPlaybackChanged = () => setAudioBlocked(!room.canPlaybackAudio);
|
||||
onPlaybackChanged();
|
||||
setMicOn(room.localParticipant.isMicrophoneEnabled);
|
||||
|
||||
room
|
||||
.on(RoomEvent.ParticipantConnected, refresh)
|
||||
@@ -204,6 +209,20 @@ export function PodView({
|
||||
}
|
||||
}
|
||||
|
||||
// Publish/unpublish the local mic. Enabling triggers the browser permission
|
||||
// prompt, so the button doubles as a "is my mic set up right?" check.
|
||||
async function toggleMic() {
|
||||
if (!room) return;
|
||||
setNote(null);
|
||||
try {
|
||||
const next = !micOn;
|
||||
await room.localParticipant.setMicrophoneEnabled(next);
|
||||
setMicOn(next);
|
||||
} catch (e) {
|
||||
setNote(`Could not toggle mic: ${(e as Error).message}`);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
screenTrackRef.current?.stop();
|
||||
@@ -356,7 +375,31 @@ export function PodView({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2 sm:flex sm:items-center">
|
||||
<div className="grid grid-cols-2 gap-2 sm:flex sm:flex-wrap sm:items-center">
|
||||
<Button
|
||||
variant={audioBlocked ? 'default' : 'outline'}
|
||||
onClick={() => void enableSound()}
|
||||
disabled={!room}
|
||||
>
|
||||
{audioBlocked ? (
|
||||
<VolumeXIcon data-icon="inline-start" />
|
||||
) : (
|
||||
<Volume2Icon data-icon="inline-start" />
|
||||
)}
|
||||
{audioBlocked ? 'Enable audio' : 'Audio on'}
|
||||
</Button>
|
||||
<Button
|
||||
variant={micOn ? 'default' : 'outline'}
|
||||
onClick={() => void toggleMic()}
|
||||
disabled={!room}
|
||||
>
|
||||
{micOn ? (
|
||||
<MicIcon data-icon="inline-start" />
|
||||
) : (
|
||||
<MicOffIcon data-icon="inline-start" />
|
||||
)}
|
||||
{micOn ? 'Mic on' : 'Enable mic'}
|
||||
</Button>
|
||||
<Button variant="outline" onClick={onToggleBeat} disabled={!room}>
|
||||
<Volume2Icon data-icon="inline-start" />
|
||||
{beat.on ? (beat.mine ? 'Stop audio' : `Stop (${beat.by})`) : 'Test audio'}
|
||||
@@ -399,21 +442,6 @@ export function PodView({
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{room && audioBlocked && (
|
||||
<Alert className="flex items-center justify-between gap-3">
|
||||
<div className="flex items-start gap-3">
|
||||
<Volume2Icon />
|
||||
<div>
|
||||
<AlertTitle>Sound is off</AlertTitle>
|
||||
<AlertDescription>Click to hear PodMan's voice alerts.</AlertDescription>
|
||||
</div>
|
||||
</div>
|
||||
<Button size="sm" onClick={() => void enableSound()}>
|
||||
Enable sound
|
||||
</Button>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<main className="grid flex-1 gap-5 min-[1800px]:grid-cols-[minmax(0,1fr)_340px]">
|
||||
<section className="flex min-w-0 flex-col gap-5">
|
||||
<Card className="flex-1">
|
||||
|
||||
Reference in New Issue
Block a user